Compare commits
330 Commits
devtest-20
...
devtest-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a8e84e4cc | ||
|
|
668ab78b1b | ||
|
|
c754d6a892 | ||
|
|
e9108479b5 | ||
|
|
857da21b0f | ||
|
|
73bba97aaa | ||
|
|
d6e9cdab5b | ||
|
|
1a177bc2de | ||
|
|
e0b3e631fe | ||
|
|
2518a353af | ||
|
|
989800efff | ||
|
|
c02846e2cb | ||
|
|
09db4a0e25 | ||
|
|
a85da9d86c | ||
|
|
920d00bbae | ||
|
|
62475279ee | ||
|
|
d8e979d283 | ||
|
|
299b8880dd | ||
|
|
61027e4067 | ||
|
|
a7249c10dc | ||
|
|
611d12ac78 | ||
|
|
ef9f26a60d | ||
|
|
aeaffc0a8e | ||
|
|
e3084e230e | ||
|
|
4c01c772f8 | ||
|
|
ed94f7680a | ||
|
|
408d66cdaf | ||
|
|
c4a0f2f169 | ||
|
|
26b28d26da | ||
|
|
8ded6dafd4 | ||
|
|
57a94ad667 | ||
|
|
53933a4d8f | ||
|
|
6606d7dd93 | ||
|
|
7a256dcafa | ||
|
|
7899c52b6d | ||
|
|
919c670502 | ||
|
|
aac3174efc | ||
|
|
a8d3d5c79a | ||
|
|
7c852d90fb | ||
|
|
269ce9c406 | ||
|
|
53d98ec255 | ||
|
|
7a7cd21578 | ||
|
|
2b363e4e1c | ||
|
|
b1560ae69c | ||
|
|
f3ebe07540 | ||
|
|
6cac587753 | ||
|
|
09e6cc4add | ||
|
|
5a2f91be1c | ||
|
|
25071b813e | ||
|
|
d61bd675c4 | ||
|
|
582e2774ac | ||
|
|
5a52ce5330 | ||
|
|
31c9b4fc80 | ||
|
|
4953ce314b | ||
|
|
86519bfda5 | ||
|
|
2c9a36b9a3 | ||
|
|
d3847d49ed | ||
|
|
57f40a0b20 | ||
|
|
fa05f4e4b0 | ||
|
|
754f41ecd1 | ||
|
|
4b6e1c2198 | ||
|
|
e6db2c98d0 | ||
|
|
942f1e2d9e | ||
|
|
da5c94858f | ||
|
|
77ffc20a5f | ||
|
|
d75fed3a00 | ||
|
|
2aba054fe8 | ||
|
|
3b1f4ba07f | ||
|
|
d35768e0f9 | ||
|
|
07a10069db | ||
|
|
20fe59e844 | ||
|
|
6ad5b9ebc4 | ||
|
|
dd0b08d54a | ||
|
|
1cc1f93fb0 | ||
|
|
72f1f06ebc | ||
|
|
151cea96e9 | ||
|
|
888915b53b | ||
|
|
de7a84e8ed | ||
|
|
1dd5b113c7 | ||
|
|
e9ad38667e | ||
|
|
05e8026713 | ||
|
|
5b75649888 | ||
|
|
c22392eb1e | ||
|
|
7c2a51df38 | ||
|
|
05cb9b1fbf | ||
|
|
6bba35c330 | ||
|
|
90d9ee1f5c | ||
|
|
385f01247b | ||
|
|
996029ee38 | ||
|
|
87929b3d91 | ||
|
|
e7e50cc101 | ||
|
|
d6c0926856 | ||
|
|
99ec119ffd | ||
|
|
6b2920cc91 | ||
|
|
05f933f007 | ||
|
|
80aeb5ada6 | ||
|
|
83ea65d4ff | ||
|
|
46b3b01b89 | ||
|
|
973f679939 | ||
|
|
6de1b7b915 | ||
|
|
e7ce739fec | ||
|
|
bf14a4ce80 | ||
|
|
8840a690c6 | ||
|
|
77e85e7c58 | ||
|
|
6943cf5ad5 | ||
|
|
17996dfdfc | ||
|
|
7f32776701 | ||
|
|
fce109a46d | ||
|
|
748292324a | ||
|
|
efe0de2ecb | ||
|
|
cac9940736 | ||
|
|
559b143265 | ||
|
|
a66305e282 | ||
|
|
b3aa61ee8e | ||
|
|
80436a3195 | ||
|
|
5127a6813d | ||
|
|
84eb3c54ef | ||
|
|
ea3c7a3c34 | ||
|
|
718cf37146 | ||
|
|
10f645bf77 | ||
|
|
eda9966d27 | ||
|
|
672172d1f1 | ||
|
|
a366e37014 | ||
|
|
d66e0bb22e | ||
|
|
13581c030d | ||
|
|
13a8b6bda2 | ||
|
|
3aaaa95618 | ||
|
|
e3929d7ded | ||
|
|
2c62f747d9 | ||
|
|
e08818cca1 | ||
|
|
b4670345dd | ||
|
|
0320dcdef9 | ||
|
|
8b12cce250 | ||
|
|
6bb7ab0f97 | ||
|
|
b32346d65f | ||
|
|
8f9c212921 | ||
|
|
df39d4fcc4 | ||
|
|
4daa5193b6 | ||
|
|
13596c1474 | ||
|
|
bb71b59e18 | ||
|
|
f2797c711c | ||
|
|
6b6b1e56e6 | ||
|
|
7b75a78e38 | ||
|
|
2cea4b26e8 | ||
|
|
48f4a98c6a | ||
|
|
9ddc9073c2 | ||
|
|
774f2e0852 | ||
|
|
f5a963ac47 | ||
|
|
38f1f1e5c2 | ||
|
|
4cdbf74256 | ||
|
|
bd0738c5c4 | ||
|
|
72c82cb080 | ||
|
|
b9dd59cd63 | ||
|
|
79019b06ca | ||
|
|
90b25be1b6 | ||
|
|
e4faa6b0f0 | ||
|
|
1a3dfdc67f | ||
|
|
8d2156fb30 | ||
|
|
5e032edd28 | ||
|
|
63d597e4ad | ||
|
|
4135045ca4 | ||
|
|
538623c835 | ||
|
|
3674583053 | ||
|
|
daa8c74c37 | ||
|
|
8aeec24c9b | ||
|
|
54c4a05062 | ||
|
|
466de89e17 | ||
|
|
6eaf51d450 | ||
|
|
7c8dc5d5f4 | ||
|
|
4f34d3edb3 | ||
|
|
0efdbc762d | ||
|
|
5e42c03afc | ||
|
|
58726160a9 | ||
|
|
8cd9215756 | ||
|
|
0e33640b14 | ||
|
|
a1c8cbab0b | ||
|
|
50b484df56 | ||
|
|
99facd2797 | ||
|
|
f79e1cacf0 | ||
|
|
82069db724 | ||
|
|
5a7dc385a3 | ||
|
|
49e7a33db0 | ||
|
|
90b26681eb | ||
|
|
14fc0254c6 | ||
|
|
214aa64ce3 | ||
|
|
a7bb217887 | ||
|
|
10cb8090ec | ||
|
|
1fdecb4e9d | ||
|
|
575596c9ad | ||
|
|
e11c8436bd | ||
|
|
c23efea402 | ||
|
|
8d3cec5bea | ||
|
|
87389d3051 | ||
|
|
71e3ca4493 | ||
|
|
2adee1e374 | ||
|
|
094ccf76b0 | ||
|
|
bb116034c7 | ||
|
|
da53d5b776 | ||
|
|
75fe0e524f | ||
|
|
0ded0355c1 | ||
|
|
4da14cee0a | ||
|
|
90c8e112e2 | ||
|
|
d49bf81d95 | ||
|
|
abbe16b6ec | ||
|
|
274bf06eae | ||
|
|
dcf7b6b3f6 | ||
|
|
4173e40a9d | ||
|
|
1abd272862 | ||
|
|
946bf7b9fa | ||
|
|
0d64fa549b | ||
|
|
8a7020b4ef | ||
|
|
1bc19e788c | ||
|
|
c17110dac5 | ||
|
|
d07b34e67e | ||
|
|
accecee018 | ||
|
|
1861174d38 | ||
|
|
8d5ed65feb | ||
|
|
a375f0e58a | ||
|
|
b2b639434c | ||
|
|
5eadd26f66 | ||
|
|
c0cbca26ea | ||
|
|
8fb65fd9bf | ||
|
|
9072d645fd | ||
|
|
597b8b1caa | ||
|
|
2c0d512727 | ||
|
|
87c5cc96ad | ||
|
|
fc844cfa6d | ||
|
|
1ab1c30e39 | ||
|
|
f2a1a497c7 | ||
|
|
1ddbe50b3f | ||
|
|
dd7b8b24af | ||
|
|
6dcb701d1d | ||
|
|
f67b7ad837 | ||
|
|
a4a409f39b | ||
|
|
904a5f60d1 | ||
|
|
ca8341d432 | ||
|
|
815bbc6ee8 | ||
|
|
ca8870a5cf | ||
|
|
e5da58e2b4 | ||
|
|
dd18829def | ||
|
|
41814a881d | ||
|
|
d708f46d50 | ||
|
|
11f57b2b26 | ||
|
|
60df247416 | ||
|
|
6f32196f89 | ||
|
|
d647aab7fe | ||
|
|
be88c33399 | ||
|
|
7372da150a | ||
|
|
3ec3eac160 | ||
|
|
0990caefd7 | ||
|
|
fc1786e243 | ||
|
|
7e61199458 | ||
|
|
0672553a07 | ||
|
|
8d1f72c104 | ||
|
|
6337067032 | ||
|
|
7395a3ed2d | ||
|
|
e4e1878a4b | ||
|
|
cc8908d7eb | ||
|
|
0dac4520ad | ||
|
|
cc2e369475 | ||
|
|
92189e4b50 | ||
|
|
2e8c85ff0b | ||
|
|
798aff1140 | ||
|
|
8596ce00cc | ||
|
|
b5613acad8 | ||
|
|
7e992d44c8 | ||
|
|
7a7393b9f0 | ||
|
|
f13e6fb76d | ||
|
|
3e83346915 | ||
|
|
761d3583c4 | ||
|
|
1a4b773fda | ||
|
|
930f8ab207 | ||
|
|
2e438f1da9 | ||
|
|
4627387ae3 | ||
|
|
fd69bce609 | ||
|
|
391d9030cb | ||
|
|
1ad9a4b65d | ||
|
|
c6cc2405d3 | ||
|
|
0e39cc7829 | ||
|
|
8d7e5f4663 | ||
|
|
e320bbfc87 | ||
|
|
2e104046d4 | ||
|
|
9cf38c1784 | ||
|
|
1aca6da1ea | ||
|
|
ce4d263b52 | ||
|
|
53c02eb2b9 | ||
|
|
82a2148300 | ||
|
|
b72a58b917 | ||
|
|
7f7bce50dc | ||
|
|
290e214638 | ||
|
|
ad4d6eaec9 | ||
|
|
cd9bf53e1a | ||
|
|
ac8b312140 | ||
|
|
3e849568ff | ||
|
|
5d6961619d | ||
|
|
94180f6a0a | ||
|
|
7803686aec | ||
|
|
b8e60ca8ec | ||
|
|
b985edbc29 | ||
|
|
ffdb3f86d7 | ||
|
|
c9b2a34561 | ||
|
|
e5dc0309f1 | ||
|
|
57a3ad8ae2 | ||
|
|
ad3722e19f | ||
|
|
a12d127fd6 | ||
|
|
e80ebfae35 | ||
|
|
4669d6b378 | ||
|
|
9f093da61e | ||
|
|
c0d31688c4 | ||
|
|
86698e7822 | ||
|
|
3fc5859f08 | ||
|
|
f9bb663f6b | ||
|
|
5e8121bd0b | ||
|
|
9cd6df2929 | ||
|
|
b16cbeacb6 | ||
|
|
4d46464bc6 | ||
|
|
7746dc55f0 | ||
|
|
5d9e8b56c5 | ||
|
|
d1c06365a8 | ||
|
|
b56ddc1a08 | ||
|
|
ae8c8e781f | ||
|
|
1344b1f2e3 | ||
|
|
36df25dcb4 | ||
|
|
84246d287d | ||
|
|
6d0fbfa21f | ||
|
|
034c6824de | ||
|
|
c8afa4a2a8 | ||
|
|
1a77f7320b | ||
|
|
183ece1360 | ||
|
|
035cc3da99 |
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,11 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Frequently Asked Questions
|
||||
url: https://github.com/OpenRA/OpenRA/wiki/FAQ#frequently-asked-questions
|
||||
about: Explanations for common problems and questions.
|
||||
- name: OpenRA Forum
|
||||
url: https://forum.openra.net/
|
||||
about: "Please ask questions about modding here."
|
||||
about: Please ask questions about modding here.
|
||||
- name: OpenRA Discord server
|
||||
url: https://discord.openra.net/
|
||||
about: "Join the community Discord server for community discussion and support."
|
||||
about: Join the community Discord server for community discussion and support.
|
||||
- name: OpenRA IRC Channel
|
||||
url: https://webchat.freenode.net/#openra
|
||||
about: "Join our development IRC channel on freenode for discussion of development topics."
|
||||
about: Join our development IRC channel on freenode for discussion of development topics.
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/crash-report.md
vendored
2
.github/ISSUE_TEMPLATE/crash-report.md
vendored
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: Crash report
|
||||
about: Report a game crash.
|
||||
about: Report a game crash. Check the FAQ first https://github.com/OpenRA/OpenRA/wiki/FAQ#common-issues
|
||||
title: My game crashed
|
||||
labels: Crash
|
||||
assignees: ''
|
||||
|
||||
55
.github/workflows/ci.yaml
vendored
Normal file
55
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
branches: [ bleed ]
|
||||
|
||||
jobs:
|
||||
linux-mono:
|
||||
name: Linux (mono)
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check Code
|
||||
run: |
|
||||
mono --version
|
||||
make check
|
||||
|
||||
- name: Check Mods
|
||||
run: |
|
||||
sudo apt-get install lua5.1
|
||||
make check-scripts
|
||||
make test
|
||||
|
||||
windows:
|
||||
name: Windows (Net 5.0)
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Check Code
|
||||
shell: powershell
|
||||
run: |
|
||||
# Work around runtime failures on the GH Actions runner
|
||||
dotnet nuget locals all --clear
|
||||
.\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:.
|
||||
|
||||
- name: Check Mods
|
||||
run: |
|
||||
chocolatey install lua --version 5.1.5.52
|
||||
$ENV:Path = $ENV:Path + ";C:\Program Files (x86)\Lua\5.1\"
|
||||
.\make.ps1 check-scripts
|
||||
.\make.ps1 test
|
||||
107
.github/workflows/documentation.yml
vendored
Normal file
107
.github/workflows/documentation.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: Deploy Documentation
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Git Tag'
|
||||
required: true
|
||||
default: 'release-xxxxxxxx'
|
||||
|
||||
jobs:
|
||||
wiki:
|
||||
name: Update Wiki
|
||||
if: github.repository == 'openra/openra'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
make all
|
||||
|
||||
- name: Clone Wiki
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: openra/openra.wiki
|
||||
token: ${{ secrets.DOCS_TOKEN }}
|
||||
path: wiki
|
||||
|
||||
- name: Update Wiki (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings (playtest).md"
|
||||
|
||||
- name: Update Wiki (Release)
|
||||
if: startsWith(github.event.inputs.tag, 'release-')
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
./utility.sh all --settings-docs "${GIT_TAG}" > "wiki/Settings.md"
|
||||
|
||||
- name: Push Wiki
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
cd wiki
|
||||
git config --local user.email "actions@github.com"
|
||||
git config --local user.name "GitHub Actions"
|
||||
git add --all
|
||||
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
|
||||
git push origin master
|
||||
|
||||
docs:
|
||||
name: Update docs.openra.net
|
||||
if: github.repository == 'openra/openra'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.inputs.tag }}
|
||||
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
make all
|
||||
|
||||
- name: Clone docs.openra.net
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: openra/docs
|
||||
token: ${{ secrets.DOCS_TOKEN }}
|
||||
path: docs
|
||||
|
||||
- name: Update docs.openra.net (Playtest)
|
||||
if: startsWith(github.event.inputs.tag, 'playtest-')
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
./utility.sh all --docs "${GIT_TAG}" > "docs/api/playtest/traits.md"
|
||||
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/playtest/weapons.md"
|
||||
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/playtest/lua.md"
|
||||
|
||||
- name: Update docs.openra.net (Release)
|
||||
if: startsWith(github.event.inputs.tag, 'release-')
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
./utility.sh all --docs "${GIT_TAG}" > "docs/api/release/traits.md"
|
||||
./utility.sh all --weapon-docs "${GIT_TAG}" > "docs/api/release/weapons.md"
|
||||
./utility.sh all --lua-docs "${GIT_TAG}" > "docs/api/release/lua.md"
|
||||
|
||||
- name: Push docs.openra.net
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
cd docs
|
||||
git config --local user.email "actions@github.com"
|
||||
git config --local user.name "GitHub Actions"
|
||||
git add --all
|
||||
git commit -m "Update auto-generated documentation for ${GIT_TAG}"
|
||||
git push origin master
|
||||
|
||||
88
.github/workflows/itch.yml
vendored
Normal file
88
.github/workflows/itch.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Deploy itch.io Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Git Tag'
|
||||
required: true
|
||||
default: 'release-xxxxxxxx'
|
||||
|
||||
jobs:
|
||||
itch:
|
||||
name: Deploy to itch.io
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'openra/openra'
|
||||
steps:
|
||||
- name: Download Packages
|
||||
env:
|
||||
GIT_TAG: ${{ github.event.inputs.tag }}
|
||||
run: |
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64.exe"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}-x64-winportable.zip" -O "OpenRA-${GIT_TAG}-x64-win-itch.zip"
|
||||
wget -q "https://github.com${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-${GIT_TAG}.dmg"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Dune-2000-x86_64.AppImage"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Red-Alert-x86_64.AppImage"
|
||||
wget -q "https://github.com/${{ github.repository }}/releases/download/${GIT_TAG}/OpenRA-Tiberian-Dawn-x86_64.AppImage"
|
||||
wget -q "https://raw.githubusercontent.com/${{ github.repository }}/${GIT_TAG}/packaging/.itch.toml"
|
||||
zip -u "OpenRA-${GIT_TAG}-x64-win-itch.zip" .itch.toml
|
||||
|
||||
- name: Publish Windows Installer
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: win
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}-x64.exe"
|
||||
|
||||
- name: Publish Windows Itch Bundle
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: itch
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}-x64-win-itch.zip
|
||||
|
||||
- name: Publish macOS Package
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: macos
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-${{ github.event.inputs.tag }}}.dmg"
|
||||
|
||||
- name: Publish RA AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-ra
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Red-Alert-x86_64.AppImage
|
||||
|
||||
- name: Publish TD AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-cnc
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Tiberian-Dawn-x86_64.AppImage
|
||||
|
||||
- name: Publish D2k AppImage
|
||||
uses: josephbmanley/butler-publish-itchio-action@master
|
||||
env:
|
||||
BUTLER_CREDENTIALS: ${{ secrets.BUTLER_CREDENTIALS }}
|
||||
CHANNEL: linux-d2k
|
||||
ITCH_GAME: openra
|
||||
ITCH_USER: openra-developers
|
||||
VERSION: ${{ github.event.inputs.tag }}
|
||||
PACKAGE: OpenRA-Dune-2000-x86_64.AppImage
|
||||
99
.github/workflows/packaging.yml
vendored
Normal file
99
.github/workflows/packaging.yml
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
name: Release Packaging
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'release-*'
|
||||
- 'playtest-*'
|
||||
- 'devtest-*'
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux AppImages
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Prepare Environment
|
||||
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Package AppImages
|
||||
run: |
|
||||
mkdir -p build/linux
|
||||
./packaging/linux/buildpackage.sh "${GIT_TAG}" "${PWD}/build/linux"
|
||||
|
||||
- name: Upload Packages
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/linux/*
|
||||
|
||||
macos:
|
||||
name: macOS Disk Images
|
||||
runs-on: macos-10.15
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Prepare Environment
|
||||
run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Package Disk Images
|
||||
env:
|
||||
MACOS_DEVELOPER_IDENTITY: ${{ secrets.MACOS_DEVELOPER_IDENTITY }}
|
||||
MACOS_DEVELOPER_CERTIFICATE_BASE64: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_BASE64 }}
|
||||
MACOS_DEVELOPER_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_DEVELOPER_CERTIFICATE_PASSWORD }}
|
||||
MACOS_DEVELOPER_USERNAME: ${{ secrets.MACOS_DEVELOPER_USERNAME }}
|
||||
MACOS_DEVELOPER_PASSWORD: ${{ secrets.MACOS_DEVELOPER_PASSWORD }}
|
||||
run: |
|
||||
mkdir -p build/macos
|
||||
./packaging/macos/buildpackage.sh "${GIT_TAG}" "${PWD}/build/macos"
|
||||
|
||||
- name: Upload Packages
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/macos/*
|
||||
|
||||
windows:
|
||||
name: Windows Installers
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Clone Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install .NET 5
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: '5.0.x'
|
||||
|
||||
- name: Prepare Environment
|
||||
run: |
|
||||
echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> ${GITHUB_ENV}
|
||||
sudo apt install nsis wine64
|
||||
|
||||
- name: Package Installers
|
||||
run: |
|
||||
mkdir -p build/windows
|
||||
./packaging/windows/buildpackage.sh "${GIT_TAG}" "${PWD}/build/windows"
|
||||
|
||||
- name: Upload Packages
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
file: build/windows/*
|
||||
92
.travis.yml
92
.travis.yml
@@ -1,92 +0,0 @@
|
||||
# Travis-CI Build for OpenRA
|
||||
# see travis-ci.org for details
|
||||
|
||||
language: csharp
|
||||
mono: 6.4.0
|
||||
os: linux
|
||||
dist: xenial
|
||||
|
||||
jobs:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
- os: osx
|
||||
if: tag IS present
|
||||
osx_image: xcode10
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- lua5.1
|
||||
- dpkg
|
||||
- zsync
|
||||
- imagemagick
|
||||
|
||||
# Environment variables
|
||||
env:
|
||||
secure: "C0+Hlfa0YGErxUuWV00Tj6p45otC/D3YwYFuLpi2mj1rDFn/4dgh5WRngjvdDBVbXJ3duaZ78jPHWm1jr7vn2jqj9yETsCIK9psWd38ep/FEBM0SDr6MUD89OuXk/YyvxJAE+UXF6bXg7giey09g/CwBigjMW7ynET3wNAWPHPs="
|
||||
|
||||
# Fetch dependencies
|
||||
# Run the build script
|
||||
# Check source code with StyleCop
|
||||
# call OpenRA to check for YAML errors
|
||||
# Run the NUnit tests
|
||||
script:
|
||||
- make all
|
||||
- |
|
||||
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
|
||||
make check || travis_terminate 1;
|
||||
make check-scripts || travis_terminate 1;
|
||||
make test || travis_terminate 1;
|
||||
mono ~/.nuget/packages/nunit.consolerunner/3.11.1/tools/nunit3-console.exe --noresult OpenRA.Test.dll || travis_terminate 1;
|
||||
fi
|
||||
|
||||
# Only watch the development branch and tagged release.
|
||||
branches:
|
||||
only:
|
||||
- /^release-.*$/
|
||||
- /^playtest-.*$/
|
||||
- /^devtest-.*$/
|
||||
- /^prep-.*$/
|
||||
- bleed
|
||||
|
||||
# Notify developers when build passed/failed.
|
||||
notifications:
|
||||
irc:
|
||||
if: repo = OpenRA/OpenRA
|
||||
template:
|
||||
- "%{repository}#%{build_number} %{commit} %{author}: %{message} %{build_url}"
|
||||
channels:
|
||||
- "irc.freenode.net#openra"
|
||||
use_notice: true
|
||||
skip_join: true
|
||||
|
||||
before_deploy:
|
||||
- if [[ "${TRAVIS_OS_NAME}" == "linux" ]]; then
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis-common_3.04-1_all.deb;
|
||||
wget https://mirrors.edge.kernel.org/ubuntu/pool/universe/n/nsis/nsis_3.04-1_amd64.deb;
|
||||
sudo dpkg -i nsis-common_3.04-1_all.deb;
|
||||
sudo dpkg -i nsis_3.04-1_amd64.deb;
|
||||
echo ${TRAVIS_REPO_SLUG};
|
||||
if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
cd packaging && ./update-wiki.sh ${TRAVIS_TAG} && cd ..;
|
||||
fi;
|
||||
fi
|
||||
- export PATH=${PATH}:${HOME}/usr/bin
|
||||
- DOTVERSION=`echo ${TRAVIS_TAG} | sed "s/-/\\./g"`
|
||||
- cd packaging
|
||||
- mkdir build
|
||||
- ./package-all.sh ${TRAVIS_TAG} ${PWD}/build/
|
||||
- if [[ "${TRAVIS_REPO_SLUG}" == "OpenRA/OpenRA" ]]; then
|
||||
./upload-itch.sh ${TRAVIS_TAG} ${PWD}/build/;
|
||||
fi
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
token: ${GH_DEPLOY_API_KEY}
|
||||
file_glob: true
|
||||
file: build/*
|
||||
skip_cleanup: true
|
||||
on:
|
||||
all_branches: true
|
||||
tags: true
|
||||
12
.vscode/launch.json
vendored
12
.vscode/launch.json
vendored
@@ -13,7 +13,8 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=cnc"]
|
||||
"args": ["Game.Mod=cnc"],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
"name": "Launch (RA)",
|
||||
@@ -27,7 +28,8 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ra"]
|
||||
"args": ["Game.Mod=ra"],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
"name": "Launch (D2k)",
|
||||
@@ -41,7 +43,8 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=d2k"]
|
||||
"args": ["Game.Mod=d2k"],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
{
|
||||
"name": "Launch (TS)",
|
||||
@@ -55,7 +58,8 @@
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/OpenRA.Game.exe",
|
||||
"cwd": "${workspaceRoot}",
|
||||
"args": ["Game.Mod=ts"]
|
||||
"args": ["Game.Mod=ts"],
|
||||
"preLaunchTask": "build",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@@ -145,6 +145,7 @@ Also thanks to:
|
||||
* Tirili
|
||||
* Tomas Einarsson (Mesacer)
|
||||
* Tom van Leth (tovl)
|
||||
* Trevor Nichols (ocdi)
|
||||
* Tristan Keating (Kilkakon)
|
||||
* Tristan Mühlbacher (MicroBit)
|
||||
* UnknownProgrammer
|
||||
@@ -188,6 +189,8 @@ distributed under MIT License.
|
||||
Using Json.NET developed by James Newton-King
|
||||
distributed under MIT License.
|
||||
|
||||
Using ANGLE distributed under the BS3 3-Clause license.
|
||||
|
||||
This site or product includes IP2Location LITE data
|
||||
available from http://www.ip2location.com.
|
||||
|
||||
|
||||
10
INSTALL.md
10
INSTALL.md
@@ -8,18 +8,16 @@ Windows
|
||||
|
||||
Compiling OpenRA requires the following dependencies:
|
||||
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
|
||||
* [.NET Framework 4.7.2 (Developer Pack)](https://dotnet.microsoft.com/download/dotnet-framework/net472) (or via Visual Studio 2017)
|
||||
* [.NET Core 2.2 SDK](https://dotnet.microsoft.com/download/dotnet-core/2.2) (or via Visual Studio 2017)
|
||||
* [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) (or via Visual Studio)
|
||||
|
||||
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with MSBuild or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
To compile OpenRA, open the `OpenRA.sln` solution in the main folder, build it from the command-line with `dotnet` or use the Makefile analogue command `make all` scripted in PowerShell syntax.
|
||||
|
||||
Run the game with `launch-game.cmd`. It can be handed arguments that specify the exact mod one wishes to run, for example, run `launch-game.cmd Game.Mod=ra` to launch Red Alert, `launch-game.cmd Game.Mod=cnc` to start Tiberian dawn or `launch-game.cmd Game.Mod=d2k` to launch Dune 2000.
|
||||
|
||||
Linux
|
||||
=====
|
||||
|
||||
Mono, version 5.18 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
Mono, version 6.4 or later, is required to compile OpenRA. You can add the [upstream mono repository](https://www.mono-project.com/download/stable/#download-lin) for your distro to obtain the latest version if your system packages are not sufficient.
|
||||
|
||||
To compile OpenRA, run `make` from the command line. After this one can run the game with `./launch-game.sh`. It is also possible to specify the mod you wish to run from the command line, e.g. with `./launch-game.sh Game.Mod=ts` if you wish to try the experimental Tiberian Sun mod.
|
||||
|
||||
@@ -99,7 +97,7 @@ macOS
|
||||
=====
|
||||
|
||||
Before compiling OpenRA you must install the following dependencies:
|
||||
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
|
||||
* [Mono >= 6.4](https://www.mono-project.com/download/stable/#download-mac)
|
||||
|
||||
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.
|
||||
|
||||
|
||||
344
Makefile
344
Makefile
@@ -12,45 +12,34 @@
|
||||
# to check the engine and official mod dlls for code style violations, run:
|
||||
# make check
|
||||
#
|
||||
# to install, run:
|
||||
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000, run:
|
||||
# make [prefix=/foo] [bindir=/bar/bin] install
|
||||
#
|
||||
# to install Linux startup scripts, desktop files and icons:
|
||||
# make install-linux-shortcuts [DEBUG=false]
|
||||
# to install Linux startup scripts, desktop files, icons, and MIME metadata
|
||||
# make install-linux-shortcuts
|
||||
#
|
||||
# to install the engine and common mod files (omitting the default mods):
|
||||
# make install-engine
|
||||
# make install-dependencies
|
||||
# make install-common-mod-files
|
||||
#
|
||||
# to uninstall, run:
|
||||
# make uninstall
|
||||
# to install Linux AppStream metadata
|
||||
# make install-linux-appdata
|
||||
#
|
||||
# for help, run:
|
||||
# make help
|
||||
#
|
||||
# to start the game, run:
|
||||
# openra
|
||||
|
||||
############################## TOOLCHAIN ###############################
|
||||
#
|
||||
# List of .NET assemblies that we can guarantee exist
|
||||
# OpenRA.Game.dll is a harmless false positive that we can ignore
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.Game.exe OpenRA.Utility.exe OpenRA.Platforms.Default.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll OpenRA.Game.dll
|
||||
WHITELISTED_OPENRA_ASSEMBLIES = OpenRA.dll OpenRA.Utility.dll OpenRA.Server.dll OpenRA.Platforms.Default.dll OpenRA.Game.dll OpenRA.Mods.Common.dll OpenRA.Mods.Cnc.dll OpenRA.Mods.D2k.dll
|
||||
|
||||
# These are explicitly shipped alongside our core files by the packaging script
|
||||
WHITELISTED_THIRDPARTY_ASSEMBLIES = ICSharpCode.SharpZipLib.dll FuzzyLogicLibrary.dll Eluant.dll BeaconLib.dll Open.Nat.dll SDL2-CS.dll OpenAL-CS.Core.dll DiscordRPC.dll Newtonsoft.Json.dll
|
||||
|
||||
# These are shipped in our custom minimal mono runtime and also available in the full system-installed .NET/mono stack
|
||||
# This list *must* be kept in sync with the files packaged by the AppImageSupport and OpenRALauncherOSX repositories
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll
|
||||
|
||||
NUNIT_LIBS_PATH :=
|
||||
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
|
||||
WHITELISTED_CORE_ASSEMBLIES = mscorlib.dll System.dll System.Configuration.dll System.Core.dll System.Numerics.dll System.Security.dll System.Xml.dll Mono.Security.dll netstandard.dll Microsoft.Win32.Registry.dll System.Security.AccessControl.dll System.Security.Principal.Windows.dll System.Xml.Linq.dll System.Runtime.Serialization.dll
|
||||
|
||||
######################### UTILITIES/SETTINGS ###########################
|
||||
#
|
||||
# install locations
|
||||
# Install locations for local installs and downstream packaging
|
||||
prefix ?= /usr/local
|
||||
datarootdir ?= $(prefix)/share
|
||||
datadir ?= $(datarootdir)
|
||||
@@ -60,27 +49,21 @@ libdir ?= $(prefix)/lib
|
||||
gameinstalldir ?= $(libdir)/openra
|
||||
|
||||
BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
|
||||
DATA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
|
||||
DATA_INSTALL_DIR = $(DESTDIR)$(datadir)
|
||||
OPENRA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
|
||||
|
||||
# install tools
|
||||
# Toolchain
|
||||
CWD = $(shell pwd)
|
||||
MSBUILD = msbuild -verbosity:m -nologo
|
||||
MONO = mono
|
||||
RM = rm
|
||||
RM_R = $(RM) -r
|
||||
RM_F = $(RM) -f
|
||||
RM_RF = $(RM) -rf
|
||||
CP = cp
|
||||
CP_R = $(CP) -r
|
||||
INSTALL = install
|
||||
INSTALL_DIR = $(INSTALL) -d
|
||||
INSTALL_PROGRAM = $(INSTALL) -m755
|
||||
INSTALL_DATA = $(INSTALL) -m644
|
||||
|
||||
# Toolchain
|
||||
MSBUILD = msbuild -verbosity:m -nologo
|
||||
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
|
||||
|
||||
# Enable 32 bit builds while generating the windows installer
|
||||
WIN32 = false
|
||||
|
||||
# dependencies
|
||||
# Detect target platform for dependencies if not given by the user
|
||||
ifndef TARGETPLATFORM
|
||||
UNAME_S := $(shell uname -s)
|
||||
UNAME_M := $(shell uname -m)
|
||||
@@ -95,242 +78,73 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# program targets
|
||||
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
|
||||
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.dll
|
||||
|
||||
check-scripts:
|
||||
@echo
|
||||
@echo "Checking for Lua syntax errors..."
|
||||
@luac -p $(shell find mods/*/maps/* -iname '*.lua')
|
||||
@luac -p $(shell find lua/* -iname '*.lua')
|
||||
|
||||
check:
|
||||
@echo
|
||||
@echo "Compiling in debug mode..."
|
||||
@$(MSBUILD) -t:build -p:Configuration=Debug
|
||||
@echo
|
||||
@echo "Checking runtime assemblies..."
|
||||
@mono --debug OpenRA.Utility.exe all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)
|
||||
@echo
|
||||
@echo "Checking for explicit interface violations..."
|
||||
@mono --debug OpenRA.Utility.exe all --check-explicit-interfaces
|
||||
@echo
|
||||
@echo "Checking for incorrect conditional trait interface overrides..."
|
||||
@mono --debug OpenRA.Utility.exe all --check-conditional-trait-interface-overrides
|
||||
|
||||
test: core
|
||||
@echo
|
||||
@echo "Testing Tiberian Sun mod MiniYAML..."
|
||||
@mono --debug OpenRA.Utility.exe ts --check-yaml
|
||||
@echo
|
||||
@echo "Testing Dune 2000 mod MiniYAML..."
|
||||
@mono --debug OpenRA.Utility.exe d2k --check-yaml
|
||||
@echo
|
||||
@echo "Testing Tiberian Dawn mod MiniYAML..."
|
||||
@mono --debug OpenRA.Utility.exe cnc --check-yaml
|
||||
@echo
|
||||
@echo "Testing Red Alert mod MiniYAML..."
|
||||
@mono --debug OpenRA.Utility.exe ra --check-yaml
|
||||
|
||||
########################## MAKE/INSTALL RULES ##########################
|
||||
##################### DEVELOPMENT BUILDS AND TESTS #####################
|
||||
#
|
||||
all: core
|
||||
|
||||
core:
|
||||
all:
|
||||
@command -v $(firstword $(MSBUILD)) >/dev/null || (echo "OpenRA requires the '$(MSBUILD)' tool provided by Mono >= 5.18."; exit 1)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM)
|
||||
@$(MSBUILD) -t:Build -restore -p:Configuration=Release -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
|
||||
ifeq ($(TARGETPLATFORM), unix-generic)
|
||||
@./configure-system-libraries.sh
|
||||
endif
|
||||
@./fetch-geoip.sh
|
||||
|
||||
clean:
|
||||
@-$(RM_F) *.config IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
@-$(RM_F) *.exe *.dll *.dll.config *.so *.dylib ./OpenRA*/*.dll *.pdb mods/**/*.dll mods/**/*.pdb *.resources
|
||||
@-$(RM_RF) ./*/bin ./*/obj
|
||||
@ $(MSBUILD) -t:clean
|
||||
@-$(RM_RF) ./bin ./*/bin ./*/obj
|
||||
@$(MSBUILD) -t:Clean -p:Mono=true
|
||||
@-$(RM_F) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP
|
||||
|
||||
check:
|
||||
@echo
|
||||
@echo "Compiling in debug mode..."
|
||||
@$(MSBUILD) -t:build -restore -p:Configuration=Debug -p:TargetPlatform=$(TARGETPLATFORM) -p:Mono=true -p:DefineConstants="MONO"
|
||||
@echo
|
||||
@echo "Checking runtime assemblies..."
|
||||
@$(OPENRA_UTILITY) all --check-runtime-assemblies $(WHITELISTED_OPENRA_ASSEMBLIES) $(WHITELISTED_THIRDPARTY_ASSEMBLIES) $(WHITELISTED_CORE_ASSEMBLIES)
|
||||
@echo
|
||||
@echo "Checking for explicit interface violations..."
|
||||
@$(OPENRA_UTILITY) all --check-explicit-interfaces
|
||||
@echo
|
||||
@echo "Checking for incorrect conditional trait interface overrides..."
|
||||
@$(OPENRA_UTILITY) all --check-conditional-trait-interface-overrides
|
||||
|
||||
check-scripts:
|
||||
@echo
|
||||
@echo "Checking for Lua syntax errors..."
|
||||
@luac -p $(shell find mods/*/maps/* -iname '*.lua')
|
||||
@luac -p $(shell find lua/* -iname '*.lua')
|
||||
@luac -p $(shell find mods/*/bits/scripts/* -iname '*.lua')
|
||||
|
||||
test: all
|
||||
@echo
|
||||
@echo "Testing Tiberian Sun mod MiniYAML..."
|
||||
@$(OPENRA_UTILITY) ts --check-yaml
|
||||
@echo
|
||||
@echo "Testing Dune 2000 mod MiniYAML..."
|
||||
@$(OPENRA_UTILITY) d2k --check-yaml
|
||||
@echo
|
||||
@echo "Testing Tiberian Dawn mod MiniYAML..."
|
||||
@$(OPENRA_UTILITY) cnc --check-yaml
|
||||
@echo
|
||||
@echo "Testing Red Alert mod MiniYAML..."
|
||||
@$(OPENRA_UTILITY) ra --check-yaml
|
||||
|
||||
############# 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
|
||||
@echo "$(VERSION)" > VERSION
|
||||
@for i in $? ; do \
|
||||
awk '{sub("Version:.*$$","Version: $(VERSION)"); print $0}' $${i} > $${i}.tmp && \
|
||||
awk '{sub("/[^/]*: User$$", "/$(VERSION): User"); print $0}' $${i}.tmp > $${i} && \
|
||||
rm $${i}.tmp; \
|
||||
done
|
||||
@sh -c '. ./packaging/functions.sh; set_engine_version $(VERSION) .'
|
||||
@sh -c '. ./packaging/functions.sh; set_mod_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'
|
||||
|
||||
install: core install-engine install-common-mod-files install-default-mods
|
||||
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
|
||||
install:
|
||||
@sh -c '. ./packaging/functions.sh; install_assemblies_mono $(CWD) $(OPENRA_INSTALL_DIR) $(TARGETPLATFORM) True True True'
|
||||
@sh -c '. ./packaging/functions.sh; install_data $(CWD) $(OPENRA_INSTALL_DIR) cnc d2k ra'
|
||||
|
||||
install-linux-shortcuts: install-linux-scripts install-linux-icons install-linux-desktop
|
||||
|
||||
install-dependencies:
|
||||
ifeq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dll "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), linux-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.so "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.so "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
ifeq ($(TARGETPLATFORM), osx-x64)
|
||||
@-echo "Installing OpenRA dependencies to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) soft_oal.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) SDL2.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) freetype6.dylib "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) lua51.dylib "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
|
||||
install-engine:
|
||||
@-echo "Installing OpenRA engine to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Game.exe "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Server.exe "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Utility.exe "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) OpenRA.Platforms.Default.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
ifneq ($(TARGETPLATFORM), $(filter $(TARGETPLATFORM),win-x86 win-x64))
|
||||
@$(INSTALL_DATA) OpenRA.Platforms.Default.dll.config "$(DATA_INSTALL_DIR)"
|
||||
endif
|
||||
@$(INSTALL_DATA) VERSION "$(DATA_INSTALL_DIR)/VERSION"
|
||||
@$(INSTALL_DATA) AUTHORS "$(DATA_INSTALL_DIR)/AUTHORS"
|
||||
@$(INSTALL_DATA) COPYING "$(DATA_INSTALL_DIR)/COPYING"
|
||||
@$(INSTALL_DATA) IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP "$(DATA_INSTALL_DIR)/IP2LOCATION-LITE-DB1.IPV6.BIN.ZIP"
|
||||
|
||||
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
|
||||
@$(CP_R) lua "$(DATA_INSTALL_DIR)"
|
||||
@$(CP) SDL2-CS* "$(DATA_INSTALL_DIR)"
|
||||
@$(CP) OpenAL-CS* "$(DATA_INSTALL_DIR)"
|
||||
@$(CP) Eluant* "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) ICSharpCode.SharpZipLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) BeaconLib.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) DiscordRPC.dll "$(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
|
||||
|
||||
install-common-mod-files:
|
||||
@-echo "Installing OpenRA common mod files to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)/mods"
|
||||
@$(CP_R) mods/common "$(DATA_INSTALL_DIR)/mods/"
|
||||
@$(INSTALL_PROGRAM) mods/common/OpenRA.Mods.Common.dll "$(DATA_INSTALL_DIR)/mods/common"
|
||||
@$(INSTALL_PROGRAM) mods/common/OpenRA.Mods.Cnc.dll "$(DATA_INSTALL_DIR)/mods/common"
|
||||
@$(INSTALL_DATA) "global mix database.dat" "$(DATA_INSTALL_DIR)/global mix database.dat"
|
||||
|
||||
install-default-mods:
|
||||
@-echo "Installing OpenRA default mods to $(DATA_INSTALL_DIR)"
|
||||
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)/mods"
|
||||
@$(CP_R) mods/cnc "$(DATA_INSTALL_DIR)/mods/"
|
||||
@$(CP_R) mods/ra "$(DATA_INSTALL_DIR)/mods/"
|
||||
@$(CP_R) mods/d2k "$(DATA_INSTALL_DIR)/mods/"
|
||||
@$(INSTALL_PROGRAM) mods/d2k/OpenRA.Mods.D2k.dll "$(DATA_INSTALL_DIR)/mods/d2k"
|
||||
@$(CP_R) mods/modcontent "$(DATA_INSTALL_DIR)/mods/"
|
||||
|
||||
install-linux-icons:
|
||||
for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps"; \
|
||||
$(INSTALL_DATA) packaging/artwork/ra_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(INSTALL_DATA) packaging/artwork/d2k_$$SIZE.png "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
done
|
||||
$(INSTALL_DIR) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps"
|
||||
$(INSTALL_DATA) packaging/artwork/ra_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
$(INSTALL_DATA) packaging/artwork/cnc_scalable.svg "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
|
||||
install-linux-desktop:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
|
||||
@sed 's/{MODID}/ra/g' packaging/linux/openra.desktop.in | sed 's/{MODNAME}/Red Alert/g' | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-ra.desktop
|
||||
@$(INSTALL_DATA) packaging/linux/openra-ra.desktop "$(DESTDIR)$(datadir)/applications"
|
||||
@sed 's/{MODID}/cnc/g' packaging/linux/openra.desktop.in | sed 's/{MODNAME}/Tiberian Dawn/g' | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-cnc.desktop
|
||||
@$(INSTALL_DATA) packaging/linux/openra-cnc.desktop "$(DESTDIR)$(datadir)/applications"
|
||||
@sed 's/{MODID}/d2k/g' packaging/linux/openra.desktop.in | sed 's/{MODNAME}/Dune 2000/g' | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-d2k.desktop
|
||||
@$(INSTALL_DATA) packaging/linux/openra-d2k.desktop "$(DESTDIR)$(datadir)/applications"
|
||||
@-$(RM) packaging/linux/openra-ra.desktop packaging/linux/openra-cnc.desktop packaging/linux/openra-d2k.desktop
|
||||
|
||||
install-linux-mime:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/mime/packages/"
|
||||
@sed 's/{MODID}/ra/g' packaging/linux/openra-mimeinfo.xml.in | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-mimeinfo.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-mimeinfo.xml "$(DESTDIR)$(datadir)/mime/packages/openra-ra.xml"
|
||||
@sed 's/{MODID}/cnc/g' packaging/linux/openra-mimeinfo.xml.in | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-mimeinfo.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-mimeinfo.xml "$(DESTDIR)$(datadir)/mime/packages/openra-cnc.xml"
|
||||
@sed 's/{MODID}/d2k/g' packaging/linux/openra-mimeinfo.xml.in | sed 's/{TAG}/$(VERSION)/g' > packaging/linux/openra-mimeinfo.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-mimeinfo.xml "$(DESTDIR)$(datadir)/mime/packages/openra-d2k.xml"
|
||||
install-linux-shortcuts:
|
||||
@sh -c '. ./packaging/functions.sh; install_linux_shortcuts $(CWD) $(OPENRA_INSTALL_DIR) $(BIN_INSTALL_DIR) $(DATA_INSTALL_DIR) $(VERSION) cnc d2k ra'
|
||||
|
||||
install-linux-appdata:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MODID}/ra/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Red Alert/g' | sed 's/{SCREENSHOT_RA}/ type="default"/g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-ra.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-ra.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MODID}/cnc/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Tiberian Dawn/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}/ type="default"/g' | sed 's/{SCREENSHOT_D2K}//g'> packaging/linux/openra-cnc.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-cnc.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@sed 's/{MODID}/d2k/g' packaging/linux/openra.appdata.xml.in | sed 's/{MOD_NAME}/Dune 2000/g' | sed 's/{SCREENSHOT_RA}//g' | sed 's/{SCREENSHOT_CNC}//g' | sed 's/{SCREENSHOT_D2K}/ type="default"/g'> packaging/linux/openra-d2k.appdata.xml
|
||||
@$(INSTALL_DATA) packaging/linux/openra-d2k.appdata.xml "$(DESTDIR)$(datadir)/appdata/"
|
||||
@-$(RM) packaging/linux/openra-ra.appdata.xml packaging/linux/openra-cnc.appdata.xml packaging/linux/openra-d2k.appdata.xml
|
||||
|
||||
install-man-page:
|
||||
@$(INSTALL_DIR) "$(DESTDIR)$(mandir)/man6/"
|
||||
@mono --debug OpenRA.Utility.exe all --man-page > openra.6
|
||||
@$(INSTALL_DATA) openra.6 "$(DESTDIR)$(mandir)/man6/"
|
||||
@-$(RM) openra.6
|
||||
|
||||
install-linux-scripts:
|
||||
ifeq ($(DEBUG), $(filter $(DEBUG),false no n off 0))
|
||||
@sed 's/{DEBUG}//' packaging/linux/openra.in | sed 's|{GAME_INSTALL_DIR}|$(gameinstalldir)|' | sed 's|{BIN_DIR}|$(bindir)|' > packaging/linux/openra.debug.in
|
||||
@sed 's/{DEBUG}//' packaging/linux/openra-server.in | sed 's|{GAME_INSTALL_DIR}|$(gameinstalldir)|' | sed 's|{BIN_DIR}|$(bindir)|' > packaging/linux/openra-server.debug.in
|
||||
else
|
||||
@sed 's/{DEBUG}/--debug/' packaging/linux/openra.in | sed 's|{GAME_INSTALL_DIR}|$(gameinstalldir)|' | sed 's|{BIN_DIR}|$(bindir)|' > packaging/linux/openra.debug.in
|
||||
@sed 's/{DEBUG}/--debug/' packaging/linux/openra-server.in | sed 's|{GAME_INSTALL_DIR}|$(gameinstalldir)|' | sed 's|{BIN_DIR}|$(bindir)|' > packaging/linux/openra-server.debug.in
|
||||
endif
|
||||
|
||||
@sed 's/{MODID}/ra/g' packaging/linux/openra.debug.in | sed 's/{TAG}/$(VERSION)/g' | sed 's/{MODNAME}/Red Alert/g' > packaging/linux/openra-ra
|
||||
@sed 's/{MODID}/cnc/g' packaging/linux/openra.debug.in | sed 's/{TAG}/$(VERSION)/g' | sed 's/{MODNAME}/Tiberian Dawn/g' > packaging/linux/openra-cnc
|
||||
@sed 's/{MODID}/d2k/g' packaging/linux/openra.debug.in | sed 's/{TAG}/$(VERSION)/g' | sed 's/{MODNAME}/Dune 2000/g' > packaging/linux/openra-d2k
|
||||
|
||||
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-ra "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-cnc "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-d2k "$(BIN_INSTALL_DIR)"
|
||||
@-$(RM) packaging/linux/openra-ra packaging/linux/openra-cnc packaging/linux/openra-d2k packaging/linux/openra.debug.in
|
||||
|
||||
@sed 's/{MODID}/ra/g' packaging/linux/openra-server.debug.in | sed 's/{MODNAME}/Red Alert/g' > packaging/linux/openra-ra-server
|
||||
@sed 's/{MODID}/cnc/g' packaging/linux/openra-server.debug.in | sed 's/{MODNAME}/Tiberian Dawn/g' > packaging/linux/openra-cnc-server
|
||||
@sed 's/{MODID}/d2k/g' packaging/linux/openra-server.debug.in | sed 's/{MODNAME}/Dune 2000/g' > packaging/linux/openra-d2k-server
|
||||
|
||||
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-ra-server "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-cnc-server "$(BIN_INSTALL_DIR)"
|
||||
@$(INSTALL_PROGRAM) -m +rx packaging/linux/openra-d2k-server "$(BIN_INSTALL_DIR)"
|
||||
@-$(RM) packaging/linux/openra-ra-server packaging/linux/openra-cnc-server packaging/linux/openra-d2k-server packaging/linux/openra-server.debug.in
|
||||
|
||||
uninstall:
|
||||
@-$(RM_R) "$(DATA_INSTALL_DIR)"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-ra"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-ra-server"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-cnc"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-cnc-server"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-d2k"
|
||||
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-d2k-server"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-ra.desktop"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-cnc.desktop"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-d2k.desktop"
|
||||
@-for SIZE in 16x16 32x32 48x48 64x64 128x128; do \
|
||||
$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-ra.png"; \
|
||||
$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-cnc.png"; \
|
||||
$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/$$SIZE/apps/openra-d2k.png"; \
|
||||
done
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-ra.svg"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/scalable/apps/openra-cnc.svg"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/mime/packages/openra-ra.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/mime/packages/openra-cnc.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/mime/packages/openra-d2k.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/appdata/openra-ra.appdata.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/appdata/openra-cnc.appdata.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(datadir)/appdata/openra-d2k.appdata.xml"
|
||||
@-$(RM_F) "$(DESTDIR)$(mandir)/man6/openra.6"
|
||||
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) $(DATA_INSTALL_DIR) cnc d2k ra'
|
||||
|
||||
help:
|
||||
@echo 'to compile, run:'
|
||||
@@ -345,22 +159,14 @@ help:
|
||||
@echo 'to check the engine and official mod dlls for code style violations, run:'
|
||||
@echo ' make test'
|
||||
@echo
|
||||
@echo 'to install, run:'
|
||||
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
|
||||
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000 run:'
|
||||
@echo ' make [prefix=/foo] install'
|
||||
@echo
|
||||
@echo 'to install Linux startup scripts, desktop files and icons'
|
||||
@echo ' make install-linux-shortcuts [DEBUG=false]'
|
||||
@echo 'to install Linux startup scripts, desktop files, icons, and MIME metadata'
|
||||
@echo ' make install-linux-shortcuts'
|
||||
@echo
|
||||
@echo ' to install the engine and common mod files (omitting the default mods):'
|
||||
@echo ' make install-engine'
|
||||
@echo ' make install-dependencies'
|
||||
@echo ' make install-common-mod-files'
|
||||
@echo
|
||||
@echo 'to uninstall, run:'
|
||||
@echo ' make uninstall'
|
||||
@echo
|
||||
@echo 'to start the game, run:'
|
||||
@echo ' openra'
|
||||
@echo 'to install Linux AppStream metadata'
|
||||
@echo ' make install-linux-appdata'
|
||||
|
||||
########################### MAKEFILE SETTINGS ##########################
|
||||
#
|
||||
@@ -368,4 +174,4 @@ help:
|
||||
|
||||
.SUFFIXES:
|
||||
|
||||
.PHONY: check-scripts check test all core clean version install install-linux-shortcuts install-dependencies install-engine install-common-mod-files install-default-mods install-linux-icons install-linux-desktop install-linux-mime install-linux-appdata install-man-page install-linux-scripts uninstall help
|
||||
.PHONY: all clean check check-scripts test version install install-linux-shortcuts install-linux-appdata help
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Activities
|
||||
public readonly Color Color;
|
||||
public readonly Sprite Tile;
|
||||
|
||||
public TargetLineNode(Target target, Color color, Sprite tile = null)
|
||||
public TargetLineNode(in Target target, Color color, Sprite tile = null)
|
||||
{
|
||||
// Note: Not all activities are drawable. In that case, pass Target.Invalid as target,
|
||||
// if "yield break" in TargetLineNode(Actor self) is not feasible.
|
||||
|
||||
@@ -102,6 +102,7 @@ namespace OpenRA
|
||||
|
||||
readonly IFacing facing;
|
||||
readonly IHealth health;
|
||||
readonly IResolveOrder[] resolveOrders;
|
||||
readonly IRenderModifier[] renderModifiers;
|
||||
readonly IRender[] renders;
|
||||
readonly IMouseBounds[] mouseBounds;
|
||||
@@ -109,9 +110,10 @@ namespace OpenRA
|
||||
readonly IDefaultVisibility defaultVisibility;
|
||||
readonly INotifyBecomingIdle[] becomingIdles;
|
||||
readonly INotifyIdle[] tickIdles;
|
||||
readonly ITargetablePositions[] targetablePositions;
|
||||
readonly IEnumerable<ITargetablePositions> enabledTargetablePositions;
|
||||
WPos[] staticTargetablePositions;
|
||||
bool created;
|
||||
bool setStaticTargetablePositions;
|
||||
|
||||
internal Actor(World world, string name, TypeDictionary initDict)
|
||||
{
|
||||
@@ -139,42 +141,61 @@ namespace OpenRA
|
||||
throw new NotImplementedException("No rules definition for unit " + name);
|
||||
|
||||
Info = world.Map.Rules.Actors[name];
|
||||
foreach (var trait in Info.TraitsInConstructOrder())
|
||||
|
||||
IPositionable positionable = null;
|
||||
var resolveOrdersList = new List<IResolveOrder>();
|
||||
var renderModifiersList = new List<IRenderModifier>();
|
||||
var rendersList = new List<IRender>();
|
||||
var mouseBoundsList = new List<IMouseBounds>();
|
||||
var visibilityModifiersList = new List<IVisibilityModifier>();
|
||||
var becomingIdlesList = new List<INotifyBecomingIdle>();
|
||||
var tickIdlesList = new List<INotifyIdle>();
|
||||
var targetablesList = new List<ITargetable>();
|
||||
var targetablePositionsList = new List<ITargetablePositions>();
|
||||
var syncHashesList = new List<SyncHash>();
|
||||
|
||||
foreach (var traitInfo in Info.TraitsInConstructOrder())
|
||||
{
|
||||
AddTrait(trait.Create(init));
|
||||
var trait = traitInfo.Create(init);
|
||||
AddTrait(trait);
|
||||
|
||||
// Some traits rely on properties provided by IOccupySpace in their initialization,
|
||||
// so we must ready it now, we cannot wait until all traits have finished construction.
|
||||
if (trait is IOccupySpaceInfo)
|
||||
OccupiesSpace = Trait<IOccupySpace>();
|
||||
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
|
||||
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
|
||||
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
|
||||
// Note: The blocks are required to limit the scope of the t's, so we make an exception to our normal style
|
||||
// rules for spacing in order to keep these assignments compact and readable.
|
||||
{ if (trait is IPositionable t) positionable = t; }
|
||||
{ if (trait is IOccupySpace t) OccupiesSpace = t; }
|
||||
{ if (trait is IEffectiveOwner t) EffectiveOwner = t; }
|
||||
{ if (trait is IFacing t) facing = t; }
|
||||
{ if (trait is IHealth t) health = t; }
|
||||
{ if (trait is IResolveOrder t) resolveOrdersList.Add(t); }
|
||||
{ if (trait is IRenderModifier t) renderModifiersList.Add(t); }
|
||||
{ if (trait is IRender t) rendersList.Add(t); }
|
||||
{ if (trait is IMouseBounds t) mouseBoundsList.Add(t); }
|
||||
{ if (trait is IVisibilityModifier t) visibilityModifiersList.Add(t); }
|
||||
{ if (trait is IDefaultVisibility t) defaultVisibility = t; }
|
||||
{ if (trait is INotifyBecomingIdle t) becomingIdlesList.Add(t); }
|
||||
{ if (trait is INotifyIdle t) tickIdlesList.Add(t); }
|
||||
{ if (trait is ITargetable t) targetablesList.Add(t); }
|
||||
{ if (trait is ITargetablePositions t) targetablePositionsList.Add(t); }
|
||||
{ if (trait is ISync t) syncHashesList.Add(new SyncHash(t)); }
|
||||
}
|
||||
|
||||
resolveOrders = resolveOrdersList.ToArray();
|
||||
renderModifiers = renderModifiersList.ToArray();
|
||||
renders = rendersList.ToArray();
|
||||
mouseBounds = mouseBoundsList.ToArray();
|
||||
visibilityModifiers = visibilityModifiersList.ToArray();
|
||||
becomingIdles = becomingIdlesList.ToArray();
|
||||
tickIdles = tickIdlesList.ToArray();
|
||||
Targetables = targetablesList.ToArray();
|
||||
var targetablePositions = targetablePositionsList.ToArray();
|
||||
enabledTargetablePositions = targetablePositions.Where(Exts.IsTraitEnabled);
|
||||
SyncHashes = syncHashesList.ToArray();
|
||||
|
||||
setStaticTargetablePositions = positionable == null && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled);
|
||||
}
|
||||
|
||||
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
|
||||
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
|
||||
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
|
||||
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
|
||||
facing = TraitOrDefault<IFacing>();
|
||||
health = TraitOrDefault<IHealth>();
|
||||
renderModifiers = TraitsImplementing<IRenderModifier>().ToArray();
|
||||
renders = TraitsImplementing<IRender>().ToArray();
|
||||
mouseBounds = TraitsImplementing<IMouseBounds>().ToArray();
|
||||
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
|
||||
defaultVisibility = Trait<IDefaultVisibility>();
|
||||
becomingIdles = TraitsImplementing<INotifyBecomingIdle>().ToArray();
|
||||
tickIdles = TraitsImplementing<INotifyIdle>().ToArray();
|
||||
Targetables = TraitsImplementing<ITargetable>().ToArray();
|
||||
targetablePositions = TraitsImplementing<ITargetablePositions>().ToArray();
|
||||
world.AddFrameEndTask(w =>
|
||||
{
|
||||
// Caching this in a AddFrameEndTask, because trait construction order might cause problems if done directly at creation time.
|
||||
// All actors that can move or teleport should have IPositionable, if not it's pretty safe to assume the actor is completely immobile and
|
||||
// all targetable positions can be cached if all ITargetablePositions have no conditional requirements.
|
||||
if (!Info.HasTraitInfo<IPositionableInfo>() && targetablePositions.Any() && targetablePositions.All(tp => tp.AlwaysEnabled))
|
||||
staticTargetablePositions = targetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray();
|
||||
});
|
||||
|
||||
SyncHashes = TraitsImplementing<ISync>().Select(sync => new SyncHash(sync)).ToArray();
|
||||
}
|
||||
|
||||
internal void Initialize(bool addToWorld = true)
|
||||
@@ -208,7 +229,12 @@ namespace OpenRA
|
||||
foreach (var notify in allObserverNotifiers)
|
||||
notify(this, readOnlyConditionCache);
|
||||
|
||||
// TODO: Some traits may need initialization after being notified of initial condition state.
|
||||
// All actors that can move or teleport should have IPositionable, if not it's pretty safe to assume the actor is completely immobile and
|
||||
// all targetable positions can be cached if all ITargetablePositions have no conditional requirements.
|
||||
if (setStaticTargetablePositions)
|
||||
staticTargetablePositions = enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this)).ToArray();
|
||||
|
||||
// TODO: Other traits may need initialization after being notified of initial condition state.
|
||||
|
||||
// TODO: A post condition initialization notification phase may allow queueing activities instead.
|
||||
// The initial activity should run before any activities queued by INotifyCreated.Created
|
||||
@@ -404,6 +430,12 @@ namespace OpenRA
|
||||
});
|
||||
}
|
||||
|
||||
public void ResolveOrder(Order order)
|
||||
{
|
||||
foreach (var r in resolveOrders)
|
||||
r.ResolveOrder(this, order);
|
||||
}
|
||||
|
||||
// TODO: move elsewhere.
|
||||
public void ChangeOwner(Player newOwner)
|
||||
{
|
||||
@@ -507,9 +539,8 @@ namespace OpenRA
|
||||
if (staticTargetablePositions != null)
|
||||
return staticTargetablePositions;
|
||||
|
||||
var enabledTargetablePositionTraits = targetablePositions.Where(Exts.IsTraitEnabled);
|
||||
if (enabledTargetablePositionTraits.Any())
|
||||
return enabledTargetablePositionTraits.SelectMany(tp => tp.TargetablePositions(this));
|
||||
if (enabledTargetablePositions.Any())
|
||||
return enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
|
||||
|
||||
return new[] { CenterPosition };
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace OpenRA
|
||||
mods[key] = mod;
|
||||
}
|
||||
|
||||
internal void Register(Manifest mod, string launchPath, ModRegistration registration)
|
||||
internal void Register(Manifest mod, string launchPath, IEnumerable<string> launchArgs, ModRegistration registration)
|
||||
{
|
||||
if (mod.Metadata.Hidden)
|
||||
return;
|
||||
@@ -133,7 +133,7 @@ namespace OpenRA
|
||||
new MiniYamlNode("Version", mod.Metadata.Version),
|
||||
new MiniYamlNode("Title", mod.Metadata.Title),
|
||||
new MiniYamlNode("LaunchPath", launchPath),
|
||||
new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id)
|
||||
new MiniYamlNode("LaunchArgs", new[] { "Game.Mod=" + mod.Id }.Concat(launchArgs).JoinWith(", "))
|
||||
}));
|
||||
|
||||
using (var stream = mod.Package.GetStream("icon.png"))
|
||||
|
||||
@@ -120,7 +120,14 @@ namespace OpenRA
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k)
|
||||
where V : new()
|
||||
{
|
||||
return d.GetOrAdd(k, _ => new V());
|
||||
return d.GetOrAdd(k, new V());
|
||||
}
|
||||
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, V v)
|
||||
{
|
||||
if (!d.TryGetValue(k, out var ret))
|
||||
d.Add(k, ret = v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)
|
||||
|
||||
@@ -435,7 +435,7 @@ namespace OpenRA
|
||||
ret.SetValue(GetValue(fieldName, fieldType.GetElementType(), parts[i].Trim(), field), i);
|
||||
return ret;
|
||||
}
|
||||
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(HashSet<>))
|
||||
else if (fieldType.IsGenericType && (fieldType.GetGenericTypeDefinition() == typeof(HashSet<>) || fieldType.GetGenericTypeDefinition() == typeof(List<>)))
|
||||
{
|
||||
var set = Activator.CreateInstance(fieldType);
|
||||
if (value == null)
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace OpenRA
|
||||
return ((Array)v).Cast<object>().Select(FormatValue).JoinWith(", ");
|
||||
}
|
||||
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(HashSet<>))
|
||||
if (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(HashSet<>) || t.GetGenericTypeDefinition() == typeof(List<>)))
|
||||
{
|
||||
return ((System.Collections.IEnumerable)v).Cast<object>().Select(FormatValue).JoinWith(", ");
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
// Raw directories are the easiest and one of the most common cases, so try these first
|
||||
var resolvedPath = Platform.ResolvePath(filename);
|
||||
if (!filename.Contains("|") && Directory.Exists(resolvedPath))
|
||||
if (!resolvedPath.Contains("|") && Directory.Exists(resolvedPath))
|
||||
return new Folder(resolvedPath);
|
||||
|
||||
// Children of another package require special handling
|
||||
@@ -295,7 +295,7 @@ namespace OpenRA.FileSystem
|
||||
public static string ResolveAssemblyPath(string path, Manifest manifest, InstalledMods installedMods)
|
||||
{
|
||||
var explicitSplit = path.IndexOf('|');
|
||||
if (explicitSplit > 0)
|
||||
if (explicitSplit > 0 && !path.StartsWith("^"))
|
||||
{
|
||||
var parent = path.Substring(0, explicitSplit);
|
||||
var filename = path.Substring(explicitSplit + 1);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.FileSystem
|
||||
{
|
||||
@@ -32,10 +33,11 @@ namespace OpenRA.FileSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
|
||||
yield return Path.GetFileName(filename);
|
||||
foreach (var filename in Directory.GetDirectories(path))
|
||||
yield return Path.GetFileName(filename);
|
||||
// Order may vary on different file systems and it matters for hashing.
|
||||
return Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly)
|
||||
.Concat(Directory.GetDirectories(path))
|
||||
.Select(Path.GetFileName)
|
||||
.OrderBy(f => f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Runtime;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using OpenRA.Graphics;
|
||||
@@ -72,10 +73,10 @@ namespace OpenRA
|
||||
return om;
|
||||
}
|
||||
|
||||
static string TimestampedFilename(bool includemilliseconds = false)
|
||||
public static string TimestampedFilename(bool includemilliseconds = false, string extra = "")
|
||||
{
|
||||
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
|
||||
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
return ModData.Manifest.Id + extra + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
static void JoinInner(OrderManager om)
|
||||
@@ -170,11 +171,13 @@ namespace OpenRA
|
||||
|
||||
worldRenderer = new WorldRenderer(ModData, OrderManager.World);
|
||||
|
||||
// Proactively collect memory during loading to reduce peak memory.
|
||||
GC.Collect();
|
||||
|
||||
using (new PerfTimer("LoadComplete"))
|
||||
OrderManager.World.LoadComplete(worldRenderer);
|
||||
|
||||
// Proactively collect memory during loading to reduce peak memory.
|
||||
GC.Collect();
|
||||
|
||||
if (OrderManager.GameStarted)
|
||||
@@ -189,6 +192,14 @@ namespace OpenRA
|
||||
worldRenderer.RefreshPalette();
|
||||
Cursor.SetCursor("default");
|
||||
|
||||
// Now loading is completed, now is the ideal time to run a GC and compact the LOH.
|
||||
// - All the temporary garbage created during loading can be collected.
|
||||
// - Live objects are likely to live for the length of the game or longer,
|
||||
// thus promoting them into a higher generation is not an issue.
|
||||
// - We can remove any fragmentation in the LOH caused by temporary loading garbage.
|
||||
// - A loading screen is visible, so a delay won't matter to the user.
|
||||
// Much better to clean up now then to drop frames during gameplay for GC pauses.
|
||||
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
@@ -251,18 +262,24 @@ namespace OpenRA
|
||||
|
||||
public static void InitializeSettings(Arguments args)
|
||||
{
|
||||
Settings = new Settings(Platform.ResolvePath(Path.Combine(Platform.SupportDirPrefix, "settings.yaml")), args);
|
||||
Settings = new Settings(Path.Combine(Platform.SupportDir, "settings.yaml"), args);
|
||||
}
|
||||
|
||||
public static RunStatus InitializeAndRun(string[] args)
|
||||
{
|
||||
Initialize(new Arguments(args));
|
||||
|
||||
// Proactively collect memory during loading to reduce peak memory.
|
||||
GC.Collect();
|
||||
return Run();
|
||||
}
|
||||
|
||||
static void Initialize(Arguments args)
|
||||
{
|
||||
var engineDirArg = args.GetValue("Engine.EngineDir", null);
|
||||
if (!string.IsNullOrEmpty(engineDirArg))
|
||||
Platform.OverrideEngineDir(engineDirArg);
|
||||
|
||||
var supportDirArg = args.GetValue("Engine.SupportDir", null);
|
||||
if (!string.IsNullOrEmpty(supportDirArg))
|
||||
Platform.OverrideSupportDir(supportDirArg);
|
||||
@@ -272,7 +289,7 @@ namespace OpenRA
|
||||
// Load the engine version as early as possible so it can be written to exception logs
|
||||
try
|
||||
{
|
||||
EngineVersion = File.ReadAllText(Platform.ResolvePath(Path.Combine(".", "VERSION"))).Trim();
|
||||
EngineVersion = File.ReadAllText(Path.Combine(Platform.EngineDir, "VERSION")).Trim();
|
||||
}
|
||||
catch { }
|
||||
|
||||
@@ -280,6 +297,7 @@ namespace OpenRA
|
||||
EngineVersion = "Unknown";
|
||||
|
||||
Console.WriteLine("Engine version is {0}", EngineVersion);
|
||||
Console.WriteLine("Runtime: {0}", Platform.RuntimeVersion);
|
||||
|
||||
// Special case handling of Game.Mod argument: if it matches a real filesystem path
|
||||
// then we use this to override the mod search path, and replace it with the mod id
|
||||
@@ -311,10 +329,17 @@ namespace OpenRA
|
||||
Settings.Game.Platform = p;
|
||||
try
|
||||
{
|
||||
var rendererPath = Platform.ResolvePath(Path.Combine(".", "OpenRA.Platforms." + p + ".dll"));
|
||||
var assembly = Assembly.LoadFile(rendererPath);
|
||||
var rendererPath = Path.Combine(Platform.BinDir, "OpenRA.Platforms." + p + ".dll");
|
||||
|
||||
#if !MONO
|
||||
var loader = new AssemblyLoader(rendererPath);
|
||||
var platformType = loader.LoadDefaultAssembly().GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
|
||||
|
||||
#else
|
||||
var assembly = Assembly.LoadFile(rendererPath);
|
||||
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
|
||||
#endif
|
||||
|
||||
if (platformType == null)
|
||||
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
|
||||
|
||||
@@ -341,7 +366,7 @@ namespace OpenRA
|
||||
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
|
||||
var modSearchPaths = modSearchArg != null ?
|
||||
FieldLoader.GetValue<string[]>("Engine.ModsPath", modSearchArg) :
|
||||
new[] { Path.Combine(".", "mods") };
|
||||
new[] { Path.Combine(Platform.EngineDir, "mods") };
|
||||
|
||||
Mods = new InstalledMods(modSearchPaths, explicitModPaths);
|
||||
Console.WriteLine("Internal mods:");
|
||||
@@ -354,14 +379,24 @@ namespace OpenRA
|
||||
|
||||
if (modID != null && Mods.TryGetValue(modID, out _))
|
||||
{
|
||||
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
|
||||
var launchPath = args.GetValue("Engine.LaunchPath", null);
|
||||
var launchArgs = new List<string>();
|
||||
|
||||
// Sanitize input from platform-specific launchers
|
||||
// Process.Start requires paths to not be quoted, even if they contain spaces
|
||||
if (launchPath.First() == '"' && launchPath.Last() == '"')
|
||||
if (launchPath != null && launchPath.First() == '"' && launchPath.Last() == '"')
|
||||
launchPath = launchPath.Substring(1, launchPath.Length - 2);
|
||||
|
||||
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
|
||||
if (launchPath == null)
|
||||
{
|
||||
// When launching the assembly directly we must propagate the Engine.EngineDir argument if defined
|
||||
// Platform-specific launchers are expected to manage this internally.
|
||||
launchPath = Assembly.GetEntryAssembly().Location;
|
||||
if (!string.IsNullOrEmpty(engineDirArg))
|
||||
launchArgs.Add("Engine.EngineDir=\"" + engineDirArg + "\"");
|
||||
}
|
||||
|
||||
ExternalMods.Register(Mods[modID], launchPath, launchArgs, ModRegistration.User);
|
||||
|
||||
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
|
||||
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
|
||||
@@ -410,7 +445,7 @@ namespace OpenRA
|
||||
|
||||
ModData = new ModData(Mods[mod], Mods, true);
|
||||
|
||||
LocalPlayerProfile = new LocalPlayerProfile(Platform.ResolvePath(Path.Combine("^", Settings.Game.AuthProfile)), ModData.Manifest.Get<PlayerDatabase>());
|
||||
LocalPlayerProfile = new LocalPlayerProfile(Path.Combine(Platform.SupportDir, Settings.Game.AuthProfile), ModData.Manifest.Get<PlayerDatabase>());
|
||||
|
||||
if (!ModData.LoadScreen.BeforeLoad())
|
||||
return;
|
||||
@@ -526,7 +561,7 @@ namespace OpenRA
|
||||
using (new PerfTimer("Renderer.SaveScreenshot"))
|
||||
{
|
||||
var mod = ModData.Manifest.Metadata;
|
||||
var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version);
|
||||
var directory = Path.Combine(Platform.SupportDir, "Screenshots", ModData.Manifest.Id, mod.Version);
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
var filename = TimestampedFilename(true);
|
||||
@@ -907,9 +942,11 @@ namespace OpenRA
|
||||
AdvertiseOnline = false
|
||||
};
|
||||
|
||||
// Always connect to local games using the same loopback connection
|
||||
// Exposing multiple endpoints introduces a race condition on the client's PlayerIndex (sometimes 0, sometimes 1)
|
||||
// This would break the Restart button, which relies on the PlayerIndex always being the same for local servers
|
||||
var endpoints = new List<IPEndPoint>
|
||||
{
|
||||
new IPEndPoint(IPAddress.IPv6Loopback, 0),
|
||||
new IPEndPoint(IPAddress.Loopback, 0)
|
||||
};
|
||||
server = new Server.Server(endpoints, settings, ModData, ServerType.Local);
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace OpenRA
|
||||
/// <summary>Gets the game's duration, from the time the game started until the replay recording stopped.</summary>
|
||||
public TimeSpan Duration { get { return EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; } }
|
||||
public IList<Player> Players { get; private set; }
|
||||
public HashSet<int> DisabledSpawnPoints = new HashSet<int>();
|
||||
public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } }
|
||||
public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
|
||||
public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } }
|
||||
@@ -118,11 +119,13 @@ namespace OpenRA
|
||||
IsBot = runtimePlayer.IsBot,
|
||||
FactionName = runtimePlayer.Faction.Name,
|
||||
FactionId = runtimePlayer.Faction.InternalName,
|
||||
DisplayFactionName = runtimePlayer.DisplayFaction.Name,
|
||||
DisplayFactionId = runtimePlayer.DisplayFaction.InternalName,
|
||||
Color = runtimePlayer.Color,
|
||||
Team = client.Team,
|
||||
SpawnPoint = runtimePlayer.SpawnPoint,
|
||||
IsRandomFaction = runtimePlayer.Faction.InternalName != client.Faction,
|
||||
IsRandomSpawnPoint = runtimePlayer.SpawnPoint != client.SpawnPoint,
|
||||
IsRandomSpawnPoint = runtimePlayer.DisplaySpawnPoint == 0,
|
||||
Fingerprint = client.Fingerprint
|
||||
};
|
||||
|
||||
@@ -156,6 +159,10 @@ namespace OpenRA
|
||||
public string FactionId;
|
||||
public Color Color;
|
||||
|
||||
/// <summary>The faction (including Random, etc.) that was selected in the lobby.</summary>
|
||||
public string DisplayFactionName;
|
||||
public string DisplayFactionId;
|
||||
|
||||
/// <summary>The team ID on start-up, or 0 if the player is not part of a team.</summary>
|
||||
public int Team;
|
||||
public int SpawnPoint;
|
||||
@@ -179,6 +186,9 @@ namespace OpenRA
|
||||
/// <summary>The time when this player won or lost the game.</summary>
|
||||
public DateTime OutcomeTimestampUtc;
|
||||
|
||||
/// <summary>The frame at which this player disconnected.</summary>
|
||||
public int DisconnectFrame;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ namespace OpenRA
|
||||
|
||||
// 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, ts, mapSequences);
|
||||
new SequenceProvider(fileSystem, modData, tileSet, mapSequences);
|
||||
|
||||
var modelSequences = dr.ModelSequences;
|
||||
if (mapModelSequences != null)
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace OpenRA.GameRules
|
||||
}
|
||||
|
||||
/// <summary>Checks if the weapon is valid against (can target) the target.</summary>
|
||||
public bool IsValidAgainst(Target target, World world, Actor firedBy)
|
||||
public bool IsValidAgainst(in Target target, World world, Actor firedBy)
|
||||
{
|
||||
if (target.Type == TargetType.Actor)
|
||||
return IsValidAgainst(target.Actor, firedBy);
|
||||
@@ -220,20 +220,24 @@ namespace OpenRA.GameRules
|
||||
}
|
||||
|
||||
/// <summary>Applies all the weapon's warheads to the target.</summary>
|
||||
public void Impact(Target target, WarheadArgs args)
|
||||
public void Impact(in Target target, WarheadArgs args)
|
||||
{
|
||||
var world = args.SourceActor.World;
|
||||
foreach (var warhead in Warheads)
|
||||
{
|
||||
if (warhead.Delay > 0)
|
||||
world.AddFrameEndTask(w => w.Add(new DelayedImpact(warhead.Delay, warhead, target, args)));
|
||||
{
|
||||
// Lambdas can't use 'in' variables, so capture a copy for later
|
||||
var delayedTarget = target;
|
||||
world.AddFrameEndTask(w => w.Add(new DelayedImpact(warhead.Delay, warhead, delayedTarget, args)));
|
||||
}
|
||||
else
|
||||
warhead.DoImpact(target, args);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Applies all the weapon's warheads to the target. Only use for projectile-less, special-case impacts.</summary>
|
||||
public void Impact(Target target, Actor firedBy)
|
||||
public void Impact(in Target target, Actor firedBy)
|
||||
{
|
||||
// The impact will happen immediately at target.CenterPosition.
|
||||
var args = new WarheadArgs
|
||||
|
||||
@@ -236,15 +236,14 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
for (var i = 0; i < width; i++)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]);
|
||||
var c = palette[frame.Data[j * width + i]];
|
||||
var rgba = palette[frame.Data[j * width + i]];
|
||||
var k = 4 * (j * width + i);
|
||||
|
||||
// Convert RGBA to BGRA
|
||||
data[k] = bytes[2];
|
||||
data[k + 1] = bytes[1];
|
||||
data[k + 2] = bytes[0];
|
||||
data[k + 3] = bytes[3];
|
||||
data[k] = (byte)(rgba >> 16);
|
||||
data[k + 1] = (byte)(rgba >> 8);
|
||||
data[k + 2] = (byte)(rgba >> 0);
|
||||
data[k + 3] = (byte)(rgba >> 24);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ namespace OpenRA.Graphics
|
||||
Palette = palette;
|
||||
Name = name;
|
||||
|
||||
Frames = cache[cursorSrc].Skip(Start).ToArray();
|
||||
|
||||
if ((d.ContainsKey("Length") && d["Length"].Value == "*") || (d.ContainsKey("End") && d["End"].Value == "*"))
|
||||
Length = Frames.Length - Start;
|
||||
Length = Frames.Length;
|
||||
else if (d.ContainsKey("Length"))
|
||||
Length = Exts.ParseIntegerInvariant(d["Length"].Value);
|
||||
else if (d.ContainsKey("End"))
|
||||
@@ -40,10 +42,7 @@ namespace OpenRA.Graphics
|
||||
else
|
||||
Length = 1;
|
||||
|
||||
Frames = cache[cursorSrc]
|
||||
.Skip(Start)
|
||||
.Take(Length)
|
||||
.ToArray();
|
||||
Frames = Frames.Take(Length).ToArray();
|
||||
|
||||
if (d.ContainsKey("X"))
|
||||
{
|
||||
|
||||
@@ -79,8 +79,8 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
public ModelRenderProxy RenderAsync(
|
||||
WorldRenderer wr, IEnumerable<ModelAnimation> models, WRot camera, float scale,
|
||||
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale,
|
||||
float[] groundNormal, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
|
||||
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
|
||||
{
|
||||
if (!isInFrame)
|
||||
|
||||
@@ -17,6 +17,8 @@ namespace OpenRA
|
||||
{
|
||||
public enum GLProfile
|
||||
{
|
||||
Automatic,
|
||||
ANGLE,
|
||||
Modern,
|
||||
Embedded,
|
||||
Legacy
|
||||
@@ -24,7 +26,7 @@ namespace OpenRA
|
||||
|
||||
public interface IPlatform
|
||||
{
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile);
|
||||
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile, bool enableLegacyGL);
|
||||
ISoundEngine CreateSound(string device);
|
||||
IFont CreateFont(byte[] data);
|
||||
}
|
||||
@@ -101,8 +103,7 @@ namespace OpenRA
|
||||
{
|
||||
void Bind();
|
||||
void SetData(T[] vertices, int length);
|
||||
void SetData(T[] vertices, int start, int length);
|
||||
void SetData(IntPtr data, int start, int length);
|
||||
void SetData(T[] vertices, int offset, int start, int length);
|
||||
}
|
||||
|
||||
public interface IShader
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public interface ITintableRenderable
|
||||
{
|
||||
IRenderable WithTint(float3 newTint);
|
||||
IRenderable WithTint(in float3 newTint);
|
||||
}
|
||||
|
||||
public interface IFinalizedRenderable
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace OpenRA.Graphics
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void DrawLine(float3 start, float3 end, float width, Color startColor, Color endColor)
|
||||
public void DrawLine(in float3 start, in float3 end, float width, Color startColor, Color endColor)
|
||||
{
|
||||
var delta = (end - start) / (end - start).XY.Length;
|
||||
var corner = width / 2 * new float3(-delta.Y, delta.X, delta.Z);
|
||||
@@ -55,7 +55,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawRGBAVertices(vertices);
|
||||
}
|
||||
|
||||
public void DrawLine(float3 start, float3 end, float width, Color color)
|
||||
public void DrawLine(in float3 start, in float3 end, float width, Color color)
|
||||
{
|
||||
var delta = (end - start) / (end - start).XY.Length;
|
||||
var corner = width / 2 * new float2(-delta.Y, delta.X);
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA.Graphics
|
||||
/// Will behave badly if the lines are parallel.
|
||||
/// Z position is the average of a and b (ignores actual intersection point if it exists)
|
||||
/// </summary>
|
||||
float3 IntersectionOf(float3 a, float3 da, float3 b, float3 db)
|
||||
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);
|
||||
@@ -193,14 +193,14 @@ namespace OpenRA.Graphics
|
||||
DrawConnectedLine(vertices.Select(v => new float3(v, 0)).ToArray(), width, color, true);
|
||||
}
|
||||
|
||||
public void DrawRect(float3 tl, float3 br, float width, Color color)
|
||||
public void DrawRect(in float3 tl, in float3 br, float width, Color color)
|
||||
{
|
||||
var tr = new float3(br.X, tl.Y, tl.Z);
|
||||
var bl = new float3(tl.X, br.Y, br.Z);
|
||||
DrawPolygon(new[] { tl, tr, br, bl }, width, color);
|
||||
}
|
||||
|
||||
public void FillTriangle(float3 a, float3 b, float3 c, Color color)
|
||||
public void FillTriangle(in float3 a, in float3 b, in float3 c, Color color)
|
||||
{
|
||||
color = Util.PremultiplyAlpha(color);
|
||||
var cr = color.R / 255.0f;
|
||||
@@ -214,14 +214,14 @@ namespace OpenRA.Graphics
|
||||
parent.DrawRGBAVertices(vertices);
|
||||
}
|
||||
|
||||
public void FillRect(float3 tl, float3 br, Color color)
|
||||
public void FillRect(in float3 tl, in float3 br, Color color)
|
||||
{
|
||||
var tr = new float3(br.X, tl.Y, tl.Z);
|
||||
var bl = new float3(tl.X, br.Y, br.Z);
|
||||
FillRect(tl, tr, br, bl, color);
|
||||
}
|
||||
|
||||
public void FillRect(float3 a, float3 b, float3 c, float3 d, Color color)
|
||||
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color color)
|
||||
{
|
||||
color = Util.PremultiplyAlpha(color);
|
||||
var cr = color.R / 255.0f;
|
||||
@@ -238,7 +238,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawRGBAVertices(vertices);
|
||||
}
|
||||
|
||||
public void FillRect(float3 a, float3 b, float3 c, float3 d, Color topLeftColor, Color topRightColor, Color bottomRightColor, Color bottomLeftColor)
|
||||
public void FillRect(in float3 a, in float3 b, in float3 c, in float3 d, Color topLeftColor, Color topRightColor, Color bottomRightColor, Color bottomLeftColor)
|
||||
{
|
||||
vertices[0] = VertexWithColor(a + Offset, topLeftColor);
|
||||
vertices[1] = VertexWithColor(b + Offset, topRightColor);
|
||||
@@ -250,7 +250,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawRGBAVertices(vertices);
|
||||
}
|
||||
|
||||
static Vertex VertexWithColor(float3 xyz, Color color)
|
||||
static Vertex VertexWithColor(in float3 xyz, Color color)
|
||||
{
|
||||
color = Util.PremultiplyAlpha(color);
|
||||
var cr = color.R / 255.0f;
|
||||
@@ -261,7 +261,7 @@ namespace OpenRA.Graphics
|
||||
return new Vertex(xyz, cr, cg, cb, ca, 0, 0);
|
||||
}
|
||||
|
||||
public void FillEllipse(float3 tl, float3 br, Color color, int vertices = 32)
|
||||
public void FillEllipse(in float3 tl, in float3 br, Color color, int vertices = 32)
|
||||
{
|
||||
// TODO: Create an ellipse polygon instead
|
||||
var a = (br.X - tl.X) / 2;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 location, float3 size)
|
||||
public void DrawSprite(Sprite s, in float3 location, in float3 size)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
@@ -30,7 +30,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawSprite(s, location, 0, size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 location)
|
||||
public void DrawSprite(Sprite s, in float3 location)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
@@ -38,7 +38,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawSprite(s, location, 0, s.Size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
|
||||
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
@@ -46,7 +46,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawSprite(s, a, b, c, d);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, float3 tint)
|
||||
public void DrawSpriteWithTint(Sprite s, in float3 location, in float3 size, in float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
@@ -54,7 +54,7 @@ namespace OpenRA.Graphics
|
||||
parent.DrawSpriteWithTint(s, location, 0, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
public void DrawSpriteWithTint(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
|
||||
{
|
||||
if (s.Channel != TextureChannel.RGBA)
|
||||
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");
|
||||
|
||||
@@ -41,21 +41,20 @@ namespace OpenRA.Graphics
|
||||
|
||||
public interface ISpriteSequenceLoader
|
||||
{
|
||||
Action<string> OnMissingSpriteError { get; set; }
|
||||
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, TileSet tileSet, SpriteCache cache, MiniYamlNode node);
|
||||
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
|
||||
}
|
||||
|
||||
public class SequenceProvider : IDisposable
|
||||
{
|
||||
readonly ModData modData;
|
||||
readonly TileSet tileSet;
|
||||
readonly string tileSet;
|
||||
readonly Lazy<Sequences> sequences;
|
||||
readonly Lazy<SpriteCache> spriteCache;
|
||||
public SpriteCache SpriteCache { get { return spriteCache.Value; } }
|
||||
|
||||
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
|
||||
|
||||
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, MiniYaml additionalSequences)
|
||||
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, string tileSet, MiniYaml additionalSequences)
|
||||
{
|
||||
this.modData = modData;
|
||||
this.tileSet = tileSet;
|
||||
@@ -79,6 +78,8 @@ namespace OpenRA.Graphics
|
||||
return seq;
|
||||
}
|
||||
|
||||
public IEnumerable<string> Images { get { return sequences.Value.Keys; } }
|
||||
|
||||
public bool HasSequence(string unitName)
|
||||
{
|
||||
return sequences.Value.ContainsKey(unitName);
|
||||
@@ -104,10 +105,11 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
|
||||
var items = new Dictionary<string, UnitSequences>();
|
||||
foreach (var n in nodes)
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
// Work around the loop closure issue in older versions of C#
|
||||
var node = n;
|
||||
// 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("|");
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace OpenRA.Graphics
|
||||
|
||||
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, 0, frame.Offset); }
|
||||
public Sprite Add(byte[] src, Size size) { return Add(src, size, 0, float3.Zero); }
|
||||
public Sprite Add(byte[] src, Size size, float zRamp, float3 spriteOffset)
|
||||
public Sprite Add(byte[] src, Size size, float zRamp, in float3 spriteOffset)
|
||||
{
|
||||
// Don't bother allocating empty sprites
|
||||
if (size.Width == 0 || size.Height == 0)
|
||||
@@ -115,7 +115,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, float3 spriteOffset, float scale = 1f)
|
||||
public Sprite Allocate(Size imageSize, float zRamp, in float3 spriteOffset, float scale = 1f)
|
||||
{
|
||||
if (imageSize.Width + p.X + margin > current.Size.Width)
|
||||
{
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace OpenRA.Graphics
|
||||
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel, float scale = 1)
|
||||
: this(sheet, bounds, 0, float2.Zero, channel, BlendMode.Alpha, scale) { }
|
||||
|
||||
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
|
||||
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, in float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
|
||||
{
|
||||
Sheet = sheet;
|
||||
Bounds = bounds;
|
||||
|
||||
@@ -239,7 +239,15 @@ namespace OpenRA.Graphics
|
||||
return new int2(0, size);
|
||||
|
||||
var lines = text.Split('\n');
|
||||
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
|
||||
return new int2((int)Math.Ceiling(MaxLineWidth(lines, lineWidth)), lines.Length * size);
|
||||
}
|
||||
|
||||
static float MaxLineWidth(string[] lines, Func<string, float> lineWidth)
|
||||
{
|
||||
var maxWidth = 0f;
|
||||
foreach (var line in lines)
|
||||
maxWidth = Math.Max(maxWidth, lineWidth(line));
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
GlyphInfo CreateGlyph(char c)
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace OpenRA.Graphics
|
||||
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, tint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, tint, true, ignoreWorldTint); }
|
||||
|
||||
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
|
||||
public IRenderable WithTint(in float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
|
||||
|
||||
float3 ScreenPosition(WorldRenderer wr)
|
||||
{
|
||||
|
||||
@@ -10,17 +10,21 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
|
||||
namespace OpenRA.Graphics
|
||||
{
|
||||
public class SpriteRenderer : Renderer.IBatchRenderer
|
||||
{
|
||||
const int SheetCount = 7;
|
||||
static readonly string[] SheetIndexToTextureName = Exts.MakeArray(SheetCount, i => "Texture{0}".F(i));
|
||||
|
||||
readonly Renderer renderer;
|
||||
readonly IShader shader;
|
||||
|
||||
readonly Vertex[] vertices;
|
||||
readonly Sheet[] sheets = new Sheet[7];
|
||||
readonly Sheet[] sheets = new Sheet[SheetCount];
|
||||
|
||||
BlendMode currentBlend = BlendMode.Alpha;
|
||||
int nv = 0;
|
||||
@@ -39,7 +43,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
for (var i = 0; i < ns; i++)
|
||||
{
|
||||
shader.SetTexture("Texture{0}".F(i), sheets[i].GetTexture());
|
||||
shader.SetTexture(SheetIndexToTextureName[i], sheets[i].GetTexture());
|
||||
sheets[i] = null;
|
||||
}
|
||||
|
||||
@@ -104,43 +108,43 @@ namespace OpenRA.Graphics
|
||||
return new int2(sheetIndex, secondarySheetIndex);
|
||||
}
|
||||
|
||||
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
|
||||
internal void DrawSprite(Sprite s, in float3 location, float paletteTextureIndex, in float3 size)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, float3.Ones);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 location, PaletteReference pal)
|
||||
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal)
|
||||
{
|
||||
DrawSprite(s, location, pal.TextureIndex, s.Size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 location, PaletteReference pal, float3 size)
|
||||
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, float3 size)
|
||||
{
|
||||
DrawSprite(s, location, pal.TextureIndex, size);
|
||||
}
|
||||
|
||||
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
|
||||
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, float3.Ones, nv);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, float3 tint)
|
||||
internal void DrawSpriteWithTint(Sprite s, in float3 location, float paletteTextureIndex, in float3 size, in float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, samplers, paletteTextureIndex, nv, size, tint);
|
||||
nv += 6;
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
|
||||
public void DrawSpriteWithTint(Sprite s, in float3 location, PaletteReference pal, in float3 size, in float3 tint)
|
||||
{
|
||||
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
|
||||
}
|
||||
|
||||
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
|
||||
public void DrawSpriteWithTint(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
|
||||
{
|
||||
var samplers = SetRenderStateForSprite(s);
|
||||
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace OpenRA.Graphics
|
||||
dirtyRows.Add(uv.V);
|
||||
}
|
||||
|
||||
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
|
||||
public void Update(MPos uv, Sprite sprite, in float3 pos, bool ignoreTint)
|
||||
{
|
||||
if (sprite != null)
|
||||
{
|
||||
@@ -183,15 +183,7 @@ namespace OpenRA.Graphics
|
||||
continue;
|
||||
|
||||
var rowOffset = rowStride * row;
|
||||
|
||||
unsafe
|
||||
{
|
||||
// The compiler / language spec won't let us calculate a pointer to
|
||||
// an offset inside a generic array T[], and so we are forced to
|
||||
// calculate the start-of-row pointer here to pass in to SetData.
|
||||
fixed (Vertex* vPtr = &vertices[0])
|
||||
vertexBuffer.SetData((IntPtr)(vPtr + rowOffset), rowOffset, rowStride);
|
||||
}
|
||||
vertexBuffer.SetData(vertices, rowOffset, rowOffset, rowStride);
|
||||
}
|
||||
|
||||
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace OpenRA.Graphics
|
||||
readonly MersenneTwister random;
|
||||
TileSet tileset;
|
||||
|
||||
public Theater(TileSet tileset)
|
||||
public Theater(TileSet tileset, Action<uint, string> onMissingImage = null)
|
||||
{
|
||||
this.tileset = tileset;
|
||||
var allocated = false;
|
||||
@@ -63,9 +63,31 @@ namespace OpenRA.Graphics
|
||||
|
||||
foreach (var i in t.Value.Images)
|
||||
{
|
||||
var allFrames = frameCache[i];
|
||||
ISpriteFrame[] allFrames;
|
||||
if (onMissingImage != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
allFrames = frameCache[i];
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
onMissingImage(t.Key, i);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
allFrames = frameCache[i];
|
||||
|
||||
var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
|
||||
var indices = t.Value.Frames != null ? t.Value.Frames : Enumerable.Range(0, frameCount);
|
||||
var indices = t.Value.Frames != null ? t.Value.Frames : Exts.MakeArray(t.Value.TilesCount, j => j);
|
||||
|
||||
var start = indices.Min();
|
||||
var end = indices.Max();
|
||||
if (start < 0 || end >= frameCount)
|
||||
throw new YamlException("Template `{0}` uses frames [{1}..{2}] of {3}, but only [0..{4}] actually exist"
|
||||
.F(t.Key, start, end, i, frameCount - 1));
|
||||
|
||||
variants.Add(indices.Select(j =>
|
||||
{
|
||||
var f = allFrames[j];
|
||||
@@ -82,7 +104,7 @@ namespace OpenRA.Graphics
|
||||
if (sheetBuilder == null)
|
||||
sheetBuilder = new SheetBuilder(SheetBuilder.FrameTypeToSheetType(f.Type), allocate);
|
||||
else if (type != sheetBuilder.Type)
|
||||
throw new InvalidDataException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
||||
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
|
||||
|
||||
var s = sheetBuilder.Allocate(f.Size, zRamp, offset);
|
||||
Util.FastCopyIntoChannel(s, f.Data);
|
||||
@@ -107,6 +129,9 @@ namespace OpenRA.Graphics
|
||||
if (tileset.IgnoreTileSpriteOffsets)
|
||||
allSprites = allSprites.Select(s => new Sprite(s.Sheet, s.Bounds, s.ZRamp, new float3(float2.Zero, s.Offset.Z), s.Channel, s.BlendMode));
|
||||
|
||||
if (onMissingImage != null && !variants.Any())
|
||||
continue;
|
||||
|
||||
templates.Add(t.Value.Id, new TheaterTemplate(allSprites.ToArray(), variants.First().Count(), t.Value.Images.Length));
|
||||
}
|
||||
|
||||
@@ -116,6 +141,11 @@ namespace OpenRA.Graphics
|
||||
Sheet.ReleaseBuffer();
|
||||
}
|
||||
|
||||
public bool HasTileSprite(TerrainTile r, int? variant = null)
|
||||
{
|
||||
return TileSprite(r, variant) != missingTile;
|
||||
}
|
||||
|
||||
public Sprite TileSprite(TerrainTile r, int? variant = null)
|
||||
{
|
||||
if (!templates.TryGetValue(r.Type, out var template))
|
||||
@@ -138,10 +168,7 @@ namespace OpenRA.Graphics
|
||||
for (var x = 0; x < template.Size.X; x++)
|
||||
{
|
||||
var tile = new TerrainTile(template.Id, (byte)(i++));
|
||||
var tileInfo = tileset.GetTileInfo(tile);
|
||||
|
||||
// Empty tile
|
||||
if (tileInfo == null)
|
||||
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
|
||||
continue;
|
||||
|
||||
var sprite = TileSprite(tile);
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Graphics
|
||||
// yes, our channel order is nuts.
|
||||
static readonly int[] ChannelMasks = { 2, 1, 0, 3 };
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, float3 tint)
|
||||
public static void FastCreateQuad(Vertex[] vertices, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, in float3 size, in float3 tint)
|
||||
{
|
||||
var b = new float3(o.X + size.X, o.Y, o.Z);
|
||||
var c = new float3(o.X + size.X, o.Y + size.Y, o.Z + size.Z);
|
||||
@@ -29,9 +29,9 @@ namespace OpenRA.Graphics
|
||||
}
|
||||
|
||||
public static void FastCreateQuad(Vertex[] vertices,
|
||||
float3 a, float3 b, float3 c, float3 d,
|
||||
in float3 a, in float3 b, in float3 c, in float3 d,
|
||||
Sprite r, int2 samplers, float paletteTextureIndex,
|
||||
float3 tint, int nv)
|
||||
in float3 tint, int nv)
|
||||
{
|
||||
float sl = 0;
|
||||
float st = 0;
|
||||
|
||||
@@ -28,13 +28,13 @@ namespace OpenRA.Graphics
|
||||
// Color tint
|
||||
public readonly float R, G, B;
|
||||
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c)
|
||||
public Vertex(in float3 xyz, float s, float t, float u, float v, float p, float c)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, float3.Ones) { }
|
||||
|
||||
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
public Vertex(in float3 xyz, float s, float t, float u, float v, float p, float c, in float3 tint)
|
||||
: this(xyz.X, xyz.Y, xyz.Z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float3 tint)
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, in float3 tint)
|
||||
: this(x, y, z, s, t, u, v, p, c, tint.X, tint.Y, tint.Z) { }
|
||||
|
||||
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, float r, float g, float b)
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace OpenRA.Graphics
|
||||
// Try and find the closest cell
|
||||
if (candidates.Count > 0)
|
||||
{
|
||||
return candidates.OrderBy(uv =>
|
||||
return candidates.MinBy(uv =>
|
||||
{
|
||||
var p = map.CenterOfCell(uv.ToCPos(map.Grid.Type));
|
||||
var s = worldRenderer.ScreenPxPosition(p);
|
||||
@@ -290,7 +290,7 @@ namespace OpenRA.Graphics
|
||||
var dy = Math.Abs(s.Y - world.Y);
|
||||
|
||||
return dx * dx + dy * dy;
|
||||
}).First().ToCPos(map);
|
||||
}).ToCPos(map);
|
||||
}
|
||||
|
||||
// Something is very wrong, but lets return something that isn't completely bogus and hope the caller can recover
|
||||
@@ -314,7 +314,7 @@ 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(float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
|
||||
public int2 WorldToViewPx(in float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
|
||||
|
||||
public void Center(IEnumerable<Actor> actors)
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace OpenRA.Graphics
|
||||
{
|
||||
public sealed class WorldRenderer : IDisposable
|
||||
{
|
||||
public static readonly Func<IRenderable, int> RenderableScreenZPositionComparisonKey =
|
||||
public static readonly Func<IRenderable, int> RenderableZPositionComparisonKey =
|
||||
r => ZPosition(r.Pos, r.ZOffset);
|
||||
|
||||
public readonly Size TileSize;
|
||||
@@ -133,7 +133,8 @@ namespace OpenRA.Graphics
|
||||
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
|
||||
renderablesBuffer.AddRange(e.Render(this));
|
||||
|
||||
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
|
||||
// Renderables must be ordered using a stable sorting algorithm to avoid flickering artefacts
|
||||
foreach (var renderable in renderablesBuffer.OrderBy(RenderableZPositionComparisonKey))
|
||||
preparedRenderables.Add(renderable.PrepareRender(this));
|
||||
|
||||
// PERF: Reuse collection to avoid allocations.
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace OpenRA
|
||||
|
||||
readonly string[] reservedModuleNames =
|
||||
{
|
||||
"Metadata", "Folders", "MapFolders", "Packages", "Rules",
|
||||
"Include", "Metadata", "Folders", "MapFolders", "Packages", "Rules",
|
||||
"Sequences", "ModelSequences", "Cursors", "Chrome", "Assemblies", "ChromeLayout", "Weapons",
|
||||
"Voices", "Notifications", "Music", "Translations", "TileSets", "ChromeMetrics", "Missions", "Hotkeys",
|
||||
"ServerTraits", "LoadScreen", "SupportsMapsFrom", "SoundFormats", "SpriteFormats",
|
||||
@@ -89,7 +89,25 @@ namespace OpenRA
|
||||
{
|
||||
Id = modId;
|
||||
Package = package;
|
||||
yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary();
|
||||
|
||||
var nodes = MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml");
|
||||
for (var i = nodes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (nodes[i].Key != "Include")
|
||||
continue;
|
||||
|
||||
// Replace `Includes: filename.yaml` with the contents of filename.yaml
|
||||
var filename = nodes[i].Value.Value;
|
||||
var contents = package.GetStream(filename);
|
||||
if (contents == null)
|
||||
throw new YamlException("{0}: File `{1}` not found.".F(nodes[i].Location, filename));
|
||||
|
||||
nodes.RemoveAt(i);
|
||||
nodes.InsertRange(i, MiniYaml.FromStream(contents, filename));
|
||||
}
|
||||
|
||||
// Merge inherited overrides
|
||||
yaml = new MiniYaml(null, MiniYaml.Merge(new[] { nodes })).ToDictionary();
|
||||
|
||||
Metadata = FieldLoader.Load<ModMetadata>(yaml["Metadata"]);
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ namespace OpenRA
|
||||
public readonly MiniYaml NotificationDefinitions;
|
||||
public readonly MiniYaml TranslationDefinitions;
|
||||
|
||||
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new Dictionary<CPos, TerrainTile>();
|
||||
|
||||
// Generated data
|
||||
public readonly MapGrid Grid;
|
||||
public IReadOnlyPackage Package { get; private set; }
|
||||
@@ -287,7 +289,6 @@ namespace OpenRA
|
||||
this.modData = modData;
|
||||
var size = new Size(width, height);
|
||||
Grid = modData.Manifest.Get<MapGrid>();
|
||||
var tileRef = new TerrainTile(tileset.Templates.First().Key, 0);
|
||||
|
||||
Title = "Name your map here";
|
||||
Author = "Your name here";
|
||||
@@ -310,7 +311,7 @@ namespace OpenRA
|
||||
Tiles.CellEntryChanged += UpdateRamp;
|
||||
}
|
||||
|
||||
Tiles.Clear(tileRef);
|
||||
Tiles.Clear(tileset.DefaultTerrainTile);
|
||||
|
||||
PostInit();
|
||||
}
|
||||
@@ -331,6 +332,9 @@ namespace OpenRA
|
||||
throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, package.Name));
|
||||
|
||||
PlayerDefinitions = MiniYaml.NodesOrEmpty(yaml, "Players");
|
||||
if (PlayerDefinitions.Count > 64)
|
||||
throw new InvalidDataException("Maps must not define more than 64 players.\n File: {0}".F(package.Name));
|
||||
|
||||
ActorDefinitions = MiniYaml.NodesOrEmpty(yaml, "Actors");
|
||||
|
||||
Grid = modData.Manifest.Get<MapGrid>();
|
||||
@@ -427,12 +431,18 @@ namespace OpenRA
|
||||
foreach (var uv in AllCells.MapCoords)
|
||||
CustomTerrain[uv] = byte.MaxValue;
|
||||
|
||||
// Cache initial ramp state
|
||||
// Replace invalid tiles and cache ramp state
|
||||
var tileset = Rules.TileSet;
|
||||
foreach (var uv in AllCells)
|
||||
foreach (var uv in AllCells.MapCoords)
|
||||
{
|
||||
var tile = tileset.GetTileInfo(Tiles[uv]);
|
||||
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
|
||||
if (!tileset.TryGetTileInfo(Tiles[uv], out var info))
|
||||
{
|
||||
ReplacedInvalidTerrainTiles[uv.ToCPos(this)] = Tiles[uv];
|
||||
Tiles[uv] = tileset.DefaultTerrainTile;
|
||||
info = tileset.GetTileInfo(tileset.DefaultTerrainTile);
|
||||
}
|
||||
|
||||
Ramp[uv] = info.RampType;
|
||||
}
|
||||
|
||||
AllEdgeCells = UpdateEdgeCells();
|
||||
@@ -440,8 +450,7 @@ namespace OpenRA
|
||||
|
||||
void UpdateRamp(CPos cell)
|
||||
{
|
||||
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
|
||||
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
|
||||
Ramp[cell] = Rules.TileSet.GetTileInfo(Tiles[cell]).RampType;
|
||||
}
|
||||
|
||||
void InitializeCellProjection()
|
||||
@@ -667,32 +676,26 @@ namespace OpenRA
|
||||
Color left, right;
|
||||
var tileset = Rules.TileSet;
|
||||
var type = tileset.GetTileInfo(Tiles[uv]);
|
||||
if (type != null)
|
||||
if (type.MinColor != type.MaxColor)
|
||||
{
|
||||
if (type.MinColor != type.MaxColor)
|
||||
{
|
||||
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
}
|
||||
else
|
||||
left = right = type.MinColor;
|
||||
|
||||
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
|
||||
{
|
||||
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
|
||||
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
|
||||
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
|
||||
}
|
||||
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
|
||||
}
|
||||
else
|
||||
left = right = Color.Black;
|
||||
left = right = type.MinColor;
|
||||
|
||||
if (tileset.MinHeightColorBrightness != 1.0f || tileset.MaxHeightColorBrightness != 1.0f)
|
||||
{
|
||||
var scale = float2.Lerp(tileset.MinHeightColorBrightness, tileset.MaxHeightColorBrightness, Height[uv] * 1f / Grid.MaximumTerrainHeight);
|
||||
left = Color.FromArgb((int)(scale * left.R).Clamp(0, 255), (int)(scale * left.G).Clamp(0, 255), (int)(scale * left.B).Clamp(0, 255));
|
||||
right = Color.FromArgb((int)(scale * right.R).Clamp(0, 255), (int)(scale * right.G).Clamp(0, 255), (int)(scale * right.B).Clamp(0, 255));
|
||||
}
|
||||
|
||||
return (left, right);
|
||||
}
|
||||
|
||||
public byte[] SavePreview()
|
||||
{
|
||||
var tileset = Rules.TileSet;
|
||||
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)>();
|
||||
@@ -712,76 +715,73 @@ namespace OpenRA
|
||||
foreach (var worldimpsi in worldimpsis)
|
||||
worldimpsi.PopulateMapPreviewSignatureCells(this, worldActorInfo, null, positions);
|
||||
|
||||
using (var stream = new MemoryStream())
|
||||
var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric;
|
||||
|
||||
// Fudge the heightmap offset by adding as much extra as we need / can.
|
||||
// This tries to correct for our incorrect assumption that MPos == PPos
|
||||
var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom);
|
||||
var width = Bounds.Width;
|
||||
var height = Bounds.Height + heightOffset;
|
||||
|
||||
var bitmapWidth = width;
|
||||
if (isRectangularIsometric)
|
||||
bitmapWidth = 2 * bitmapWidth - 1;
|
||||
|
||||
var stride = bitmapWidth * 4;
|
||||
var pxStride = 4;
|
||||
var minimapData = new byte[stride * height];
|
||||
(Color Left, Color Right) terrainColor = default((Color, Color));
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
{
|
||||
var isRectangularIsometric = Grid.Type == MapGridType.RectangularIsometric;
|
||||
|
||||
// Fudge the heightmap offset by adding as much extra as we need / can.
|
||||
// This tries to correct for our incorrect assumption that MPos == PPos
|
||||
var heightOffset = Math.Min(Grid.MaximumTerrainHeight, MapSize.Y - Bounds.Bottom);
|
||||
var width = Bounds.Width;
|
||||
var height = Bounds.Height + heightOffset;
|
||||
|
||||
var bitmapWidth = width;
|
||||
if (isRectangularIsometric)
|
||||
bitmapWidth = 2 * bitmapWidth - 1;
|
||||
|
||||
var stride = bitmapWidth * 4;
|
||||
var pxStride = 4;
|
||||
var minimapData = new byte[stride * height];
|
||||
(Color Left, Color Right) terrainColor = default((Color, Color));
|
||||
|
||||
for (var y = 0; y < height; y++)
|
||||
for (var x = 0; x < width; x++)
|
||||
{
|
||||
for (var x = 0; x < width; x++)
|
||||
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
|
||||
|
||||
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
|
||||
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
|
||||
if (actorColor.A == 0)
|
||||
terrainColor = GetTerrainColorPair(uv);
|
||||
|
||||
if (isRectangularIsometric)
|
||||
{
|
||||
var uv = new MPos(x + Bounds.Left, y + Bounds.Top);
|
||||
|
||||
// FirstOrDefault will return a (MPos.Zero, Color.Transparent) if positions is empty
|
||||
var actorColor = positions.FirstOrDefault(ap => ap.Position == uv).Color;
|
||||
if (actorColor.A == 0)
|
||||
terrainColor = GetTerrainColorPair(uv);
|
||||
|
||||
if (isRectangularIsometric)
|
||||
// Odd rows are shifted right by 1px
|
||||
var dx = uv.V & 1;
|
||||
var xOffset = pxStride * (2 * x + dx);
|
||||
if (x + dx > 0)
|
||||
{
|
||||
// Odd rows are shifted right by 1px
|
||||
var dx = uv.V & 1;
|
||||
var xOffset = pxStride * (2 * x + dx);
|
||||
if (x + dx > 0)
|
||||
{
|
||||
var z = y * stride + xOffset - pxStride;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
|
||||
if (xOffset < stride)
|
||||
{
|
||||
var z = y * stride + xOffset;
|
||||
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var z = y * stride + pxStride * x;
|
||||
var z = y * stride + xOffset - pxStride;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
|
||||
if (xOffset < stride)
|
||||
{
|
||||
var z = y * stride + xOffset;
|
||||
var c = actorColor.A == 0 ? terrainColor.Right : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var z = y * stride + pxStride * x;
|
||||
var c = actorColor.A == 0 ? terrainColor.Left : actorColor;
|
||||
minimapData[z++] = c.R;
|
||||
minimapData[z++] = c.G;
|
||||
minimapData[z++] = c.B;
|
||||
minimapData[z] = c.A;
|
||||
}
|
||||
}
|
||||
|
||||
var png = new Png(minimapData, bitmapWidth, height);
|
||||
return png.Save();
|
||||
}
|
||||
|
||||
var png = new Png(minimapData, bitmapWidth, height);
|
||||
return png.Save();
|
||||
}
|
||||
|
||||
public bool Contains(CPos cell)
|
||||
@@ -1282,16 +1282,12 @@ namespace OpenRA
|
||||
throw new ArgumentOutOfRangeException("maxRange",
|
||||
"The requested range ({0}) cannot exceed the value of MaximumTileSearchRange ({1})".F(maxRange, Grid.MaximumTileSearchRange));
|
||||
|
||||
Func<CPos, bool> valid = Contains;
|
||||
if (allowOutsideBounds)
|
||||
valid = Tiles.Contains;
|
||||
|
||||
for (var i = minRange; i <= maxRange; i++)
|
||||
{
|
||||
foreach (var offset in Grid.TilesByDistance[i])
|
||||
{
|
||||
var t = offset + center;
|
||||
if (valid(t))
|
||||
if (allowOutsideBounds ? Tiles.Contains(t) : Contains(t))
|
||||
yield return t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ namespace OpenRA
|
||||
object syncRoot = new object();
|
||||
Queue<MapPreview> generateMinimap = new Queue<MapPreview>();
|
||||
|
||||
public Dictionary<string, string> StringPool { get; } = new Dictionary<string, string>();
|
||||
|
||||
public MapCache(ModData modData)
|
||||
{
|
||||
this.modData = modData;
|
||||
@@ -70,13 +72,10 @@ namespace OpenRA
|
||||
try
|
||||
{
|
||||
// HACK: If the path is inside the the support directory then we may need to create it
|
||||
if (Platform.IsPathRelativeToSupportDirectory(name))
|
||||
{
|
||||
// Assume that the path is a directory if there is not an existing file with the same name
|
||||
var resolved = Platform.ResolvePath(name);
|
||||
if (!File.Exists(resolved))
|
||||
Directory.CreateDirectory(resolved);
|
||||
}
|
||||
// Assume that the path is a directory if there is not an existing file with the same name
|
||||
var resolved = Platform.ResolvePath(name);
|
||||
if (resolved.StartsWith(Platform.SupportDir) && !File.Exists(resolved))
|
||||
Directory.CreateDirectory(resolved);
|
||||
|
||||
package = modData.ModFiles.OpenPackage(name);
|
||||
}
|
||||
@@ -142,12 +141,9 @@ namespace OpenRA
|
||||
name = name.Substring(1);
|
||||
|
||||
// Don't try to open the map directory in the support directory if it doesn't exist
|
||||
if (Platform.IsPathRelativeToSupportDirectory(name))
|
||||
{
|
||||
var resolved = Platform.ResolvePath(name);
|
||||
if (!Directory.Exists(resolved) || !File.Exists(resolved))
|
||||
continue;
|
||||
}
|
||||
var resolved = Platform.ResolvePath(name);
|
||||
if (resolved.StartsWith(Platform.SupportDir) && (!Directory.Exists(resolved) || !File.Exists(resolved)))
|
||||
continue;
|
||||
|
||||
using (var package = (IReadWritePackage)modData.ModFiles.OpenPackage(name))
|
||||
{
|
||||
|
||||
@@ -223,7 +223,7 @@ namespace OpenRA
|
||||
if (yamlStream == null)
|
||||
throw new FileNotFoundException("Required file map.yaml not present in this map");
|
||||
|
||||
yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml")).ToDictionary();
|
||||
yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml", stringPool: cache.StringPool)).ToDictionary();
|
||||
}
|
||||
|
||||
Package = p;
|
||||
|
||||
@@ -32,7 +32,19 @@ namespace OpenRA
|
||||
public bool LockColor = false;
|
||||
public Color Color = Game.ModData.Manifest.Get<DefaultPlayer>().Color;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the "Home" location, which can be used by traits and scripts to e.g. set the initial camera
|
||||
/// location or choose the map edge for reinforcements.
|
||||
/// This will usually be overridden for client (lobby slot) players with a location based on the Spawn index
|
||||
/// </summary>
|
||||
public CPos HomeLocation = CPos.Zero;
|
||||
|
||||
public bool LockSpawn = false;
|
||||
|
||||
/// <summary>
|
||||
/// Sets the initial spawn point index that is used to override the "Home" location for client (lobby slot) players.
|
||||
/// Map players always ignore this and use HomeLocation directly.
|
||||
/// </summary>
|
||||
public int Spawn = 0;
|
||||
|
||||
public bool LockTeam = false;
|
||||
|
||||
@@ -22,11 +22,24 @@ namespace OpenRA
|
||||
: base(gridType, size) { }
|
||||
|
||||
// Resolve an array index from map coordinates.
|
||||
int Index(PPos uv)
|
||||
public int Index(PPos uv)
|
||||
{
|
||||
return uv.V * Size.Width + uv.U;
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return entries[index];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
entries[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Gets or sets the layer contents using projected map coordinates.</summary>
|
||||
public T this[PPos uv]
|
||||
{
|
||||
|
||||
@@ -23,6 +23,8 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
|
||||
|
||||
public override string ToString() { return Type + "," + Index; }
|
||||
}
|
||||
|
||||
public struct ResourceTile
|
||||
|
||||
@@ -54,13 +54,6 @@ namespace OpenRA
|
||||
|
||||
readonly TerrainTileInfo[] tileInfo;
|
||||
|
||||
public TerrainTemplateInfo(ushort id, string[] images, int2 size, byte[] tiles)
|
||||
{
|
||||
Id = id;
|
||||
Images = images;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public TerrainTemplateInfo(TileSet tileSet, MiniYaml my)
|
||||
{
|
||||
FieldLoader.Load(this, my);
|
||||
@@ -72,8 +65,11 @@ namespace OpenRA
|
||||
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (!int.TryParse(node.Key, out var key) || key < 0 || key >= tileInfo.Length)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
if (!int.TryParse(node.Key, out var key))
|
||||
throw new YamlException("Tileset `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(tileSet.Id, Id, node.Key));
|
||||
|
||||
if (key < 0 || key >= tileInfo.Length)
|
||||
throw new YamlException("Tileset `{0}` template `{1}` references frame {2}, but only [0..{3}] are valid for a {4}x{5} Size template.".F(tileSet.Id, Id, key, tileInfo.Length - 1, Size.X, Size.Y));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
}
|
||||
@@ -85,8 +81,11 @@ namespace OpenRA
|
||||
var i = 0;
|
||||
foreach (var node in nodes)
|
||||
{
|
||||
if (!int.TryParse(node.Key, out var key) || key != i++)
|
||||
throw new InvalidDataException("Invalid tile key '{0}' on template '{1}' of tileset '{2}'.".F(node.Key, Id, tileSet.Id));
|
||||
if (!int.TryParse(node.Key, out var key))
|
||||
throw new YamlException("Tileset `{0}` template `{1}` defines a frame `{2}` that is not a valid integer.".F(tileSet.Id, Id, node.Key));
|
||||
|
||||
if (key != i++)
|
||||
throw new YamlException("Tileset `{0}` template `{1}` is missing a definition for frame {2}.".F(tileSet.Id, Id, i - 1));
|
||||
|
||||
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
|
||||
}
|
||||
@@ -162,14 +161,14 @@ namespace OpenRA
|
||||
.ToArray();
|
||||
|
||||
if (TerrainInfo.Length >= byte.MaxValue)
|
||||
throw new InvalidDataException("Too many terrain types.");
|
||||
throw new YamlException("Too many terrain types.");
|
||||
|
||||
for (byte i = 0; i < TerrainInfo.Length; i++)
|
||||
{
|
||||
var tt = TerrainInfo[i].Type;
|
||||
|
||||
if (terrainIndexByType.ContainsKey(tt))
|
||||
throw new InvalidDataException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
|
||||
throw new YamlException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
|
||||
|
||||
terrainIndexByType.Add(tt, i);
|
||||
}
|
||||
@@ -222,25 +221,30 @@ namespace OpenRA
|
||||
|
||||
public byte GetTerrainIndex(TerrainTile r)
|
||||
{
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return defaultWalkableTerrainIndex;
|
||||
|
||||
if (tpl.Contains(r.Index))
|
||||
{
|
||||
var tile = tpl[r.Index];
|
||||
if (tile != null && tile.TerrainType != byte.MaxValue)
|
||||
return tile.TerrainType;
|
||||
}
|
||||
var tile = Templates[r.Type][r.Index];
|
||||
if (tile.TerrainType != byte.MaxValue)
|
||||
return tile.TerrainType;
|
||||
|
||||
return defaultWalkableTerrainIndex;
|
||||
}
|
||||
|
||||
public TerrainTileInfo GetTileInfo(TerrainTile r)
|
||||
{
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl))
|
||||
return null;
|
||||
|
||||
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
|
||||
return Templates[r.Type][r.Index];
|
||||
}
|
||||
|
||||
public bool TryGetTileInfo(TerrainTile r, out TerrainTileInfo info)
|
||||
{
|
||||
if (!Templates.TryGetValue(r.Type, out var tpl) || !tpl.Contains(r.Index))
|
||||
{
|
||||
info = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
info = tpl[r.Index];
|
||||
return info != null;
|
||||
}
|
||||
|
||||
public TerrainTile DefaultTerrainTile { get { return new TerrainTile(Templates.First().Key, 0); } }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,10 @@ namespace OpenRA
|
||||
|
||||
public MiniYaml Clone()
|
||||
{
|
||||
return new MiniYaml(Value, Nodes.Select(n => n.Clone()).ToList());
|
||||
var clonedNodes = new MiniYamlNodes(Nodes.Count);
|
||||
foreach (var node in Nodes)
|
||||
clonedNodes.Add(node.Clone());
|
||||
return new MiniYaml(Value, clonedNodes);
|
||||
}
|
||||
|
||||
public Dictionary<string, MiniYaml> ToDictionary()
|
||||
@@ -148,8 +151,11 @@ namespace OpenRA
|
||||
return nd.ContainsKey(s) ? nd[s].Nodes : new List<MiniYamlNode>();
|
||||
}
|
||||
|
||||
static List<MiniYamlNode> FromLines(IEnumerable<string> lines, string filename, bool discardCommentsAndWhitespace)
|
||||
static List<MiniYamlNode> FromLines(IEnumerable<string> lines, string filename, bool discardCommentsAndWhitespace, Dictionary<string, string> stringPool)
|
||||
{
|
||||
if (stringPool == null)
|
||||
stringPool = new Dictionary<string, string>();
|
||||
|
||||
var levels = new List<List<MiniYamlNode>>();
|
||||
levels.Add(new List<MiniYamlNode>());
|
||||
|
||||
@@ -263,6 +269,10 @@ namespace OpenRA
|
||||
|
||||
if (key != null || !discardCommentsAndWhitespace)
|
||||
{
|
||||
key = key == null ? null : stringPool.GetOrAdd(key, key);
|
||||
value = value == null ? null : stringPool.GetOrAdd(value, value);
|
||||
comment = comment == null ? null : stringPool.GetOrAdd(comment, comment);
|
||||
|
||||
var nodes = new List<MiniYamlNode>();
|
||||
levels[level].Add(new MiniYamlNode(key, value, comment, nodes, location));
|
||||
|
||||
@@ -270,23 +280,33 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var nodes in levels)
|
||||
nodes.TrimExcess();
|
||||
|
||||
return levels[0];
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true)
|
||||
public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
|
||||
{
|
||||
return FromLines(File.ReadAllLines(path), path, discardCommentsAndWhitespace);
|
||||
return FromLines(File.ReadAllLines(path), path, discardCommentsAndWhitespace, stringPool);
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromStream(Stream s, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true)
|
||||
public static List<MiniYamlNode> FromStream(Stream s, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
|
||||
{
|
||||
IEnumerable<string> Lines(StreamReader reader)
|
||||
{
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
yield return line;
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(s))
|
||||
return FromString(reader.ReadToEnd(), fileName, discardCommentsAndWhitespace);
|
||||
return FromLines(Lines(reader), fileName, discardCommentsAndWhitespace, stringPool);
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> FromString(string text, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true)
|
||||
public static List<MiniYamlNode> FromString(string text, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
|
||||
{
|
||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None), fileName, discardCommentsAndWhitespace);
|
||||
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None), fileName, discardCommentsAndWhitespace, stringPool);
|
||||
}
|
||||
|
||||
public static List<MiniYamlNode> Merge(IEnumerable<List<MiniYamlNode>> sources)
|
||||
@@ -330,7 +350,7 @@ namespace OpenRA
|
||||
|
||||
static List<MiniYamlNode> ResolveInherits(string key, MiniYaml node, Dictionary<string, MiniYaml> tree, Dictionary<string, MiniYamlNode.SourceLocation> inherited)
|
||||
{
|
||||
var resolved = new List<MiniYamlNode>();
|
||||
var resolved = new List<MiniYamlNode>(node.Nodes.Count);
|
||||
|
||||
// Inheritance is tracked from parent->child, but not from child->parentsiblings.
|
||||
inherited = new Dictionary<string, MiniYamlNode.SourceLocation>(inherited);
|
||||
@@ -361,6 +381,7 @@ namespace OpenRA
|
||||
MergeIntoResolved(n, resolved, tree, inherited);
|
||||
}
|
||||
|
||||
resolved.TrimExcess();
|
||||
return resolved;
|
||||
}
|
||||
|
||||
@@ -397,6 +418,7 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
ret.TrimExcess();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -437,6 +459,7 @@ namespace OpenRA
|
||||
ret.Add(merged);
|
||||
}
|
||||
|
||||
ret.TrimExcess();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,6 @@ namespace OpenRA
|
||||
throw new InvalidOperationException("Unable to find a sequence loader for type '{0}'.".F(sequenceFormat.Type));
|
||||
|
||||
SpriteSequenceLoader = (ISpriteSequenceLoader)sequenceCtor.Invoke(new[] { this });
|
||||
SpriteSequenceLoader.OnMissingSpriteError = s => Log.Write("debug", s);
|
||||
|
||||
var modelFormat = Manifest.Get<ModelSequenceFormat>();
|
||||
var modelLoader = ObjectCreator.FindType(modelFormat.Type + "Loader");
|
||||
@@ -108,7 +107,7 @@ namespace OpenRA
|
||||
|
||||
defaultSequences = Exts.Lazy(() =>
|
||||
{
|
||||
var items = DefaultTileSets.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Value, null));
|
||||
var items = DefaultTileSets.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Key, null));
|
||||
return (IReadOnlyDictionary<string, SequenceProvider>)(new ReadOnlyDictionary<string, SequenceProvider>(items));
|
||||
});
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRA.Network
|
||||
if (lastClientFrame == -1)
|
||||
lastClientFrame = framePackets
|
||||
.Where(x => x.Value.ContainsKey(clientId))
|
||||
.Select(x => x.Key).OrderBy(x => x).LastOrDefault();
|
||||
.Select(x => x.Key).MaxByOrDefault(x => x);
|
||||
|
||||
clientQuitTimes[clientId] = lastClientFrame;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace OpenRA.Network
|
||||
|
||||
LastOrdersFrame = rs.ReadInt32();
|
||||
LastSyncFrame = rs.ReadInt32();
|
||||
lastSyncPacket = rs.ReadBytes(5);
|
||||
lastSyncPacket = rs.ReadBytes(Order.SyncHashOrderLength);
|
||||
|
||||
var globalSettings = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
|
||||
GlobalSettings = Session.Global.Deserialize(globalSettings[0].Value);
|
||||
@@ -190,6 +190,12 @@ namespace OpenRA.Network
|
||||
// Sync packet - we only care about the last value
|
||||
if (data.Length > 0 && data[0] == (byte)OrderType.SyncHash && frame > LastSyncFrame)
|
||||
{
|
||||
if (data.Length != Order.SyncHashOrderLength)
|
||||
{
|
||||
Log.Write("debug", "Dropped sync order with length {0}. Expected length {1}.".F(data.Length, Order.SyncHashOrderLength));
|
||||
return;
|
||||
}
|
||||
|
||||
LastSyncFrame = frame;
|
||||
lastSyncPacket = data;
|
||||
}
|
||||
@@ -282,7 +288,7 @@ namespace OpenRA.Network
|
||||
file.Write(BitConverter.GetBytes(MetadataMarker), 0, 4);
|
||||
file.Write(BitConverter.GetBytes(LastOrdersFrame), 0, 4);
|
||||
file.Write(BitConverter.GetBytes(LastSyncFrame), 0, 4);
|
||||
file.Write(lastSyncPacket, 0, 5);
|
||||
file.Write(lastSyncPacket, 0, Order.SyncHashOrderLength);
|
||||
|
||||
var globalSettingsNodes = new List<MiniYamlNode>() { GlobalSettings.Serialize() };
|
||||
file.WriteString(Encoding.UTF8, globalSettingsNodes.WriteToString());
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace OpenRA.Network
|
||||
"Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
|
||||
|
||||
// Current server state
|
||||
"Map", "State", "MaxPlayers", "Protected", "Authentication"
|
||||
"Map", "State", "MaxPlayers", "Protected", "Authentication", "DisabledSpawnPoints"
|
||||
};
|
||||
|
||||
public const int ProtocolVersion = 2;
|
||||
@@ -132,6 +132,9 @@ namespace OpenRA.Network
|
||||
[FieldLoader.LoadUsing("LoadClients")]
|
||||
public readonly GameClient[] Clients;
|
||||
|
||||
/// <summary>The list of spawnpoints that are disabled for this game</summary>
|
||||
public readonly int[] DisabledSpawnPoints = { };
|
||||
|
||||
public string ModLabel { get { return "{0} ({1})".F(ModTitle, Version); } }
|
||||
|
||||
static object LoadClients(MiniYaml yaml)
|
||||
@@ -226,6 +229,7 @@ namespace OpenRA.Network
|
||||
Protected = !string.IsNullOrEmpty(server.Settings.Password);
|
||||
Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Any();
|
||||
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
|
||||
DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToArray() ?? Array.Empty<int>();
|
||||
}
|
||||
|
||||
public string ToPOSTData(bool lanGame)
|
||||
|
||||
@@ -49,10 +49,13 @@ namespace OpenRA
|
||||
|
||||
public sealed class Order
|
||||
{
|
||||
// Length of orders with type OrderType.SyncHash
|
||||
public const int SyncHashOrderLength = 13;
|
||||
|
||||
public readonly string OrderString;
|
||||
public readonly Actor Subject;
|
||||
public readonly bool Queued;
|
||||
public readonly Target Target;
|
||||
public ref readonly Target Target => ref target;
|
||||
public readonly Actor[] GroupedActors;
|
||||
|
||||
public string TargetString;
|
||||
@@ -63,15 +66,18 @@ namespace OpenRA
|
||||
public OrderType Type = OrderType.Fields;
|
||||
|
||||
public bool SuppressVisualFeedback;
|
||||
public Target VisualFeedbackTarget;
|
||||
public ref readonly Target VisualFeedbackTarget => ref visualFeedbackTarget;
|
||||
|
||||
public Player Player { get { return Subject != null ? Subject.Owner : null; } }
|
||||
|
||||
Order(string orderString, Actor subject, Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
|
||||
readonly Target target;
|
||||
readonly Target visualFeedbackTarget;
|
||||
|
||||
Order(string orderString, Actor subject, in Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
|
||||
{
|
||||
OrderString = orderString ?? "";
|
||||
Subject = subject;
|
||||
Target = target;
|
||||
this.target = target;
|
||||
TargetString = targetString;
|
||||
Queued = queued;
|
||||
ExtraActors = extraActors;
|
||||
@@ -275,9 +281,15 @@ namespace OpenRA
|
||||
public Order(string orderString, Actor subject, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
|
||||
: this(orderString, subject, Target.Invalid, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
|
||||
|
||||
public Order(string orderString, Actor subject, Target target, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
|
||||
public Order(string orderString, Actor subject, in Target target, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
|
||||
: this(orderString, subject, target, null, queued, extraActors, CPos.Zero, 0, groupedActors) { }
|
||||
|
||||
public Order(string orderString, Actor subject, Target target, Target visualFeedbackTarget, bool queued)
|
||||
: this(orderString, subject, target, null, queued, null, CPos.Zero, 0, null)
|
||||
{
|
||||
this.visualFeedbackTarget = visualFeedbackTarget;
|
||||
}
|
||||
|
||||
public byte[] Serialize()
|
||||
{
|
||||
var minLength = 1 + OrderString.Length + 1;
|
||||
|
||||
@@ -31,13 +31,14 @@ namespace OpenRA.Network
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] SerializeSync(int sync)
|
||||
public static byte[] SerializeSync(int sync, ulong defeatState)
|
||||
{
|
||||
var ms = new MemoryStream(1 + 4);
|
||||
var ms = new MemoryStream(Order.SyncHashOrderLength);
|
||||
using (var writer = new BinaryWriter(ms))
|
||||
{
|
||||
writer.Write((byte)OrderType.SyncHash);
|
||||
writer.Write(sync);
|
||||
writer.Write(defeatState);
|
||||
}
|
||||
|
||||
return ms.GetBuffer();
|
||||
|
||||
@@ -47,10 +47,10 @@ namespace OpenRA.Network
|
||||
internal int GameSaveLastFrame = -1;
|
||||
internal int GameSaveLastSyncFrame = -1;
|
||||
|
||||
List<Order> localOrders = new List<Order>();
|
||||
List<Order> localImmediateOrders = new List<Order>();
|
||||
readonly List<Order> localOrders = new List<Order>();
|
||||
readonly List<Order> localImmediateOrders = new List<Order>();
|
||||
|
||||
List<ChatLine> chatCache = new List<ChatLine>();
|
||||
readonly List<ChatLine> chatCache = new List<ChatLine>();
|
||||
|
||||
public readonly ReadOnlyList<ChatLine> ChatCache;
|
||||
|
||||
@@ -123,8 +123,16 @@ namespace OpenRA.Network
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
|
||||
frameData.ClientQuit(clientId, frame);
|
||||
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
|
||||
else if (packet.Length > 4 && packet[4] == (byte)OrderType.SyncHash)
|
||||
{
|
||||
if (packet.Length != 4 + Order.SyncHashOrderLength)
|
||||
{
|
||||
Log.Write("debug", "Dropped sync order with length {0}. Expected length {1}.".F(packet.Length, 4 + Order.SyncHashOrderLength));
|
||||
return;
|
||||
}
|
||||
|
||||
CheckSync(packet);
|
||||
}
|
||||
else if (frame == 0)
|
||||
immediatePackets.Add((clientId, packet));
|
||||
else
|
||||
@@ -192,9 +200,16 @@ namespace OpenRA.Network
|
||||
UnitOrders.ProcessOrder(this, World, order.Client, order.Order);
|
||||
|
||||
if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame)
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash()));
|
||||
{
|
||||
var defeatState = 0UL;
|
||||
for (var i = 0; i < World.Players.Length; i++)
|
||||
if (World.Players[i].WinState == WinState.Lost)
|
||||
defeatState |= 1UL << i;
|
||||
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash(), defeatState));
|
||||
}
|
||||
else
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0));
|
||||
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0, 0));
|
||||
|
||||
if (generateSyncReport)
|
||||
using (new PerfSample("sync_report"))
|
||||
|
||||
@@ -55,28 +55,25 @@ namespace OpenRA.Network
|
||||
using (var rs = File.OpenRead(replayFilename))
|
||||
{
|
||||
var packets = new List<(int ClientId, byte[] Packet)>();
|
||||
|
||||
var chunk = new Chunk();
|
||||
|
||||
while (rs.Position < rs.Length)
|
||||
{
|
||||
var client = rs.ReadInt32();
|
||||
if (client == ReplayMetadata.MetaStartMarker)
|
||||
break;
|
||||
|
||||
var packetLen = rs.ReadInt32();
|
||||
var packet = rs.ReadBytes(packetLen);
|
||||
var frame = BitConverter.ToInt32(packet, 0);
|
||||
packets.Add((client, packet));
|
||||
|
||||
if (frame != int.MaxValue &&
|
||||
(!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
|
||||
if (frame != int.MaxValue && (!lastClientsFrame.ContainsKey(client) || frame > lastClientsFrame[client]))
|
||||
lastClientsFrame[client] = frame;
|
||||
|
||||
if (packet.Length == 5 && packet[4] == (byte)OrderType.Disconnect)
|
||||
continue; // disconnect
|
||||
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
|
||||
continue; // sync
|
||||
else if (frame == 0)
|
||||
if (packet.Length > 4 && (packet[4] == (byte)OrderType.Disconnect || packet[4] == (byte)OrderType.SyncHash))
|
||||
continue;
|
||||
|
||||
if (frame == 0)
|
||||
{
|
||||
// Parse replay metadata from orders stream
|
||||
var orders = packet.ToOrderList(null);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using OpenRA.FileFormats;
|
||||
@@ -25,9 +26,7 @@ namespace OpenRA.Network
|
||||
|
||||
static bool IsGameStart(byte[] data)
|
||||
{
|
||||
if (data.Length == 5 && data[4] == (byte)OrderType.Disconnect)
|
||||
return false;
|
||||
if (data.Length >= 5 && data[4] == (byte)OrderType.SyncHash)
|
||||
if (data.Length > 4 && (data[4] == (byte)OrderType.Disconnect || data[4] == (byte)OrderType.SyncHash))
|
||||
return false;
|
||||
|
||||
var frame = BitConverter.ToInt32(data, 0);
|
||||
@@ -45,7 +44,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
var filename = chooseFilename();
|
||||
var mod = Game.ModData.Manifest;
|
||||
var dir = Platform.ResolvePath(Platform.SupportDirPrefix, "Replays", mod.Id, mod.Metadata.Version);
|
||||
var dir = Path.Combine(Platform.SupportDir, "Replays", mod.Id, mod.Metadata.Version);
|
||||
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
@@ -85,6 +84,14 @@ namespace OpenRA.Network
|
||||
writer.Write(data);
|
||||
}
|
||||
|
||||
public void ReceiveFrame(int clientID, int frame, byte[] data)
|
||||
{
|
||||
var ms = new MemoryStream(4 + data.Length);
|
||||
ms.WriteArray(BitConverter.GetBytes(frame));
|
||||
ms.WriteArray(data);
|
||||
Receive(clientID, ms.GetBuffer());
|
||||
}
|
||||
|
||||
bool disposed;
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace OpenRA.Network
|
||||
// Keyed by the PlayerReference id that the slot corresponds to
|
||||
public Dictionary<string, Slot> Slots = new Dictionary<string, Slot>();
|
||||
|
||||
public HashSet<int> DisabledSpawnPoints = new HashSet<int>();
|
||||
|
||||
public Global GlobalSettings = new Global();
|
||||
|
||||
public static string AnonymizeIP(IPAddress ip)
|
||||
@@ -69,6 +71,9 @@ namespace OpenRA.Network
|
||||
var s = Slot.Deserialize(node.Value);
|
||||
session.Slots.Add(s.PlayerReference, s);
|
||||
break;
|
||||
case "DisabledSpawnPoints":
|
||||
session.DisabledSpawnPoints = FieldLoader.GetValue<HashSet<int>>("DisabledSpawnPoints", node.Value.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +272,10 @@ namespace OpenRA.Network
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sessionData = new List<MiniYamlNode>();
|
||||
var sessionData = new List<MiniYamlNode>()
|
||||
{
|
||||
new MiniYamlNode("DisabledSpawnPoints", FieldSaver.FormatValue(DisabledSpawnPoints))
|
||||
};
|
||||
|
||||
foreach (var client in Clients)
|
||||
sessionData.Add(client.Serialize());
|
||||
|
||||
@@ -27,13 +27,6 @@ namespace OpenRA.Network
|
||||
|
||||
internal static void ProcessOrder(OrderManager orderManager, World world, int clientId, Order order)
|
||||
{
|
||||
if (world != null)
|
||||
{
|
||||
if (!world.WorldActor.TraitsImplementing<IValidateOrder>().All(vo =>
|
||||
vo.OrderValidation(orderManager, world, clientId, order)))
|
||||
return;
|
||||
}
|
||||
|
||||
switch (order.OrderString)
|
||||
{
|
||||
// Server message
|
||||
@@ -46,7 +39,13 @@ namespace OpenRA.Network
|
||||
{
|
||||
var client = orderManager.LobbyInfo.ClientWithIndex(clientId);
|
||||
if (client != null)
|
||||
{
|
||||
client.State = Session.ClientState.Disconnected;
|
||||
var player = world?.FindPlayerByClient(client);
|
||||
if (player != null)
|
||||
world.OnPlayerDisconnected(player);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -302,10 +301,7 @@ namespace OpenRA.Network
|
||||
{
|
||||
var strings = node.Key.Split('@');
|
||||
if (strings[0] == "GlobalSettings")
|
||||
{
|
||||
orderManager.LobbyInfo.GlobalSettings = Session.Global.Deserialize(node.Value);
|
||||
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
|
||||
}
|
||||
}
|
||||
|
||||
SetOrderLag(orderManager);
|
||||
@@ -336,22 +332,27 @@ namespace OpenRA.Network
|
||||
|
||||
default:
|
||||
{
|
||||
if (world == null)
|
||||
break;
|
||||
|
||||
if (order.GroupedActors == null)
|
||||
ResolveOrder(order);
|
||||
ResolveOrder(order, world, orderManager, clientId);
|
||||
else
|
||||
foreach (var subject in order.GroupedActors)
|
||||
ResolveOrder(Order.FromGroupedOrder(order, subject));
|
||||
ResolveOrder(Order.FromGroupedOrder(order, subject), world, orderManager, clientId);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ResolveOrder(Order order)
|
||||
static void ResolveOrder(Order order, World world, OrderManager orderManager, int clientId)
|
||||
{
|
||||
if (order.Subject != null && !order.Subject.IsDead)
|
||||
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
|
||||
t.ResolveOrder(order.Subject, order);
|
||||
if (order.Subject == null || order.Subject.IsDead)
|
||||
return;
|
||||
|
||||
if (world.OrderValidators.All(vo => vo.OrderValidation(orderManager, world, clientId, order)))
|
||||
order.Subject.ResolveOrder(order);
|
||||
}
|
||||
|
||||
static void SetOrderLag(OrderManager o)
|
||||
|
||||
@@ -15,6 +15,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -41,26 +42,49 @@ namespace OpenRA
|
||||
var resolvedPath = FileSystem.FileSystem.ResolveAssemblyPath(path, manifest, mods);
|
||||
if (resolvedPath == null)
|
||||
throw new FileNotFoundException("Assembly `{0}` not found.".F(path));
|
||||
#if !MONO
|
||||
var loader = new AssemblyLoader(resolvedPath);
|
||||
var platformType = loader.LoadDefaultAssembly();
|
||||
assemblyList.Add(platformType);
|
||||
|
||||
// .NET doesn't provide any way of querying the metadata of an assembly without either:
|
||||
// (a) loading duplicate data into the application domain, breaking the world.
|
||||
// (b) crashing if the assembly has already been loaded.
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
|
||||
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
}
|
||||
|
||||
assemblyList.Add(assembly);
|
||||
#else
|
||||
LoadAssembly(assemblyList, resolvedPath);
|
||||
#endif
|
||||
}
|
||||
|
||||
AppDomain.CurrentDomain.AssemblyResolve += ResolveAssembly;
|
||||
assemblies = assemblyList.SelectMany(asm => asm.GetNamespaces().Select(ns => (asm, ns))).ToArray();
|
||||
}
|
||||
|
||||
void LoadAssembly(List<Assembly> assemblyList, string resolvedPath)
|
||||
{
|
||||
// .NET doesn't provide any way of querying the metadata of an assembly without either:
|
||||
// (a) loading duplicate data into the application domain, breaking the world.
|
||||
// (b) crashing if the assembly has already been loaded.
|
||||
// We can't check the internal name of the assembly, so we'll work off the data instead
|
||||
var hash = CryptoUtil.SHA1Hash(File.ReadAllBytes(resolvedPath));
|
||||
|
||||
if (!ResolvedAssemblies.TryGetValue(hash, out var assembly))
|
||||
{
|
||||
assembly = Assembly.LoadFile(resolvedPath);
|
||||
ResolvedAssemblies.Add(hash, assembly);
|
||||
|
||||
// Allow mods to use libraries.
|
||||
var assemblyPath = Path.GetDirectoryName(resolvedPath);
|
||||
if (assemblyPath != null)
|
||||
{
|
||||
foreach (var referencedAssembly in assembly.GetReferencedAssemblies())
|
||||
{
|
||||
var depedencyPath = Path.Combine(assemblyPath, referencedAssembly.Name + ".dll");
|
||||
if (File.Exists(depedencyPath))
|
||||
LoadAssembly(assemblyList, depedencyPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assemblyList.Add(assembly);
|
||||
}
|
||||
|
||||
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
|
||||
{
|
||||
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net472</TargetFramework>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
@@ -10,7 +10,7 @@
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
<RootNamespace>OpenRA</RootNamespace>
|
||||
<OutputPath>..</OutputPath>
|
||||
<OutputPath>../bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ExternalConsole>false</ExternalConsole>
|
||||
@@ -31,34 +31,18 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(TargetPlatform)' == 'win-x86'">
|
||||
<Prefer32bit>true</Prefer32bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Red Alert'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Game.Mod=ra</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Dune 2000'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Game.Mod=d2k</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Tiberian Dawn'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Game.Mod=cnc</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Tiberian Sun'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Game.Mod=ts</StartArguments>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<PackageReference Include="OpenRA-Eluant" Version="1.0.17" />
|
||||
<PackageReference Include="OpenRA-Open.NAT" Version="1.0.0" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.2.0" />
|
||||
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
<AdditionalFiles Include="Properties/launchSettings.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Mono)' == ''">
|
||||
<PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0-preview.3-runtime.20214.6" />
|
||||
<PackageReference Include="System.Runtime.Loader" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
|
||||
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
|
||||
|
||||
@@ -196,15 +196,17 @@ namespace OpenRA.Orders
|
||||
public readonly IOrderTargeter Order;
|
||||
public readonly IIssueOrder Trait;
|
||||
public readonly string Cursor;
|
||||
public readonly Target Target;
|
||||
public ref readonly Target Target => ref target;
|
||||
|
||||
public UnitOrderResult(Actor actor, IOrderTargeter order, IIssueOrder trait, string cursor, Target target)
|
||||
readonly Target target;
|
||||
|
||||
public UnitOrderResult(Actor actor, IOrderTargeter order, IIssueOrder trait, string cursor, in Target target)
|
||||
{
|
||||
Actor = actor;
|
||||
Order = order;
|
||||
Trait = trait;
|
||||
Cursor = cursor;
|
||||
Target = target;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,12 +22,14 @@ namespace OpenRA
|
||||
|
||||
public static class Platform
|
||||
{
|
||||
public const string SupportDirPrefix = "^";
|
||||
public static PlatformType CurrentPlatform { get { return currentPlatform.Value; } }
|
||||
public static readonly Guid SessionGUID = Guid.NewGuid();
|
||||
|
||||
static Lazy<PlatformType> currentPlatform = Exts.Lazy(GetCurrentPlatform);
|
||||
|
||||
static bool engineDirAccessed;
|
||||
static string engineDir;
|
||||
|
||||
static bool supportDirInitialized;
|
||||
static string systemSupportPath;
|
||||
static string legacyUserSupportPath;
|
||||
@@ -137,7 +139,7 @@ namespace OpenRA
|
||||
}
|
||||
|
||||
// Use a local directory in the game root if it exists (shared with the system support dir)
|
||||
var localSupportDir = Path.Combine(GameDir, "Support");
|
||||
var localSupportDir = Path.Combine(EngineDir, "Support");
|
||||
if (Directory.Exists(localSupportDir))
|
||||
userSupportPath = systemSupportPath = localSupportDir + Path.DirectorySeparatorChar;
|
||||
|
||||
@@ -170,7 +172,45 @@ namespace OpenRA
|
||||
userSupportPath = path;
|
||||
}
|
||||
|
||||
public static string GameDir
|
||||
public static string EngineDir
|
||||
{
|
||||
get
|
||||
{
|
||||
// Engine directory defaults to the location of the binaries,
|
||||
// unless OverrideGameDir is called during startup.
|
||||
if (!engineDirAccessed)
|
||||
engineDir = BinDir;
|
||||
|
||||
engineDirAccessed = true;
|
||||
return engineDir;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specify a custom engine directory that already exists on the filesystem.
|
||||
/// Cannot be called after Platform.EngineDir has been accessed.
|
||||
/// </summary>
|
||||
public static void OverrideEngineDir(string path)
|
||||
{
|
||||
if (engineDirAccessed)
|
||||
throw new InvalidOperationException("Attempted to override engine directory after it has already been accessed.");
|
||||
|
||||
// Note: Relative paths are interpreted as being relative to BinDir, not the current working dir.
|
||||
if (!Path.IsPathRooted(path))
|
||||
path = Path.Combine(BinDir, path);
|
||||
|
||||
if (!Directory.Exists(path))
|
||||
throw new DirectoryNotFoundException(path);
|
||||
|
||||
if (!path.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal) &&
|
||||
!path.EndsWith(Path.AltDirectorySeparatorChar.ToString(), StringComparison.Ordinal))
|
||||
path += Path.DirectorySeparatorChar;
|
||||
|
||||
engineDirAccessed = true;
|
||||
engineDir = path;
|
||||
}
|
||||
|
||||
public static string BinDir
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -189,50 +229,25 @@ namespace OpenRA
|
||||
{
|
||||
path = path.TrimEnd(' ', '\t');
|
||||
|
||||
// Paths starting with ^ are relative to the support dir
|
||||
if (IsPathRelativeToSupportDirectory(path))
|
||||
path = SupportDir + path.Substring(1);
|
||||
if (path == "^SupportDir")
|
||||
return SupportDir;
|
||||
|
||||
// Paths starting with . are relative to the game dir
|
||||
if (path == ".")
|
||||
return GameDir;
|
||||
if (path == "^EngineDir")
|
||||
return EngineDir;
|
||||
|
||||
if (path.StartsWith("./", StringComparison.Ordinal) || path.StartsWith(".\\", StringComparison.Ordinal))
|
||||
path = GameDir + path.Substring(2);
|
||||
if (path == "^BinDir")
|
||||
return BinDir;
|
||||
|
||||
if (path.StartsWith("^SupportDir|", StringComparison.Ordinal))
|
||||
path = SupportDir + path.Substring(12);
|
||||
|
||||
if (path.StartsWith("^EngineDir|", StringComparison.Ordinal))
|
||||
path = EngineDir + path.Substring(11);
|
||||
|
||||
if (path.StartsWith("^BinDir|", StringComparison.Ordinal))
|
||||
path = BinDir + path.Substring(8);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>Replace special character prefixes with full paths.</summary>
|
||||
public static string ResolvePath(params string[] path)
|
||||
{
|
||||
return ResolvePath(Path.Combine(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replace the full path prefix with the special notation characters ^ or .
|
||||
/// and transforms \ path separators to / on Windows
|
||||
/// </summary>
|
||||
public static string UnresolvePath(string path)
|
||||
{
|
||||
// Use a case insensitive comparison on windows to avoid problems
|
||||
// with inconsistent drive letter case
|
||||
var compare = CurrentPlatform == PlatformType.Windows ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
||||
if (path.StartsWith(SupportDir, compare))
|
||||
path = SupportDirPrefix + path.Substring(SupportDir.Length);
|
||||
|
||||
if (path.StartsWith(GameDir, compare))
|
||||
path = "./" + path.Substring(GameDir.Length);
|
||||
|
||||
if (CurrentPlatform == PlatformType.Windows)
|
||||
path = path.Replace('\\', '/');
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static bool IsPathRelativeToSupportDirectory(string path)
|
||||
{
|
||||
return path.StartsWith(SupportDirPrefix, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ using Eluant.ObjectBinding;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Scripting;
|
||||
using OpenRA.Support;
|
||||
using OpenRA.Traits;
|
||||
using OpenRA.Widgets;
|
||||
|
||||
@@ -56,23 +57,38 @@ namespace OpenRA
|
||||
public readonly bool NonCombatant = false;
|
||||
public readonly bool Playable = true;
|
||||
public readonly int ClientIndex;
|
||||
public readonly CPos HomeLocation;
|
||||
public readonly PlayerReference PlayerReference;
|
||||
public readonly bool IsBot;
|
||||
public readonly string BotType;
|
||||
public readonly Shroud Shroud;
|
||||
public readonly FrozenActorLayer FrozenActorLayer;
|
||||
|
||||
/// <summary>The faction (including Random, etc) that was selected in the lobby.</summary>
|
||||
/// <summary>The faction (including Random, etc.) that was selected in the lobby.</summary>
|
||||
public readonly FactionInfo DisplayFaction;
|
||||
|
||||
/// <summary>The spawn point index that was assigned for client-based players.</summary>
|
||||
public readonly int SpawnPoint;
|
||||
|
||||
/// <summary>The spawn point index (including 0 for Random) that was selected in the lobby for client-based players.</summary>
|
||||
public readonly int DisplaySpawnPoint;
|
||||
|
||||
public WinState WinState = WinState.Undefined;
|
||||
public int SpawnPoint;
|
||||
public bool HasObjectives = false;
|
||||
public bool Spectating;
|
||||
|
||||
public bool Spectating
|
||||
{
|
||||
get
|
||||
{
|
||||
// Players in mission maps must not leave the player view
|
||||
return !inMissionMap && (spectating || WinState != WinState.Undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public World World { get; private set; }
|
||||
|
||||
readonly bool inMissionMap;
|
||||
readonly bool spectating;
|
||||
readonly IUnlocksRenderPlayer[] unlockRenderPlayer;
|
||||
|
||||
// Each player is identified with a unique bit in the set
|
||||
@@ -94,19 +110,19 @@ namespace OpenRA
|
||||
|
||||
readonly StanceColors stanceColors;
|
||||
|
||||
static FactionInfo ChooseFaction(World world, string name, bool requireSelectable = true)
|
||||
public static FactionInfo ResolveFaction(string factionName, IEnumerable<FactionInfo> factionInfos, MersenneTwister playerRandom, bool requireSelectable = true)
|
||||
{
|
||||
var selectableFactions = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>()
|
||||
var selectableFactions = factionInfos
|
||||
.Where(f => !requireSelectable || f.Selectable)
|
||||
.ToList();
|
||||
|
||||
var selected = selectableFactions.FirstOrDefault(f => f.InternalName == name)
|
||||
?? selectableFactions.Random(world.SharedRandom);
|
||||
var selected = selectableFactions.FirstOrDefault(f => f.InternalName == factionName)
|
||||
?? selectableFactions.Random(playerRandom);
|
||||
|
||||
// Don't loop infinite
|
||||
for (var i = 0; i <= 10 && selected.RandomFactionMembers.Any(); i++)
|
||||
{
|
||||
var faction = selected.RandomFactionMembers.Random(world.SharedRandom);
|
||||
var faction = selected.RandomFactionMembers.Random(playerRandom);
|
||||
selected = selectableFactions.FirstOrDefault(f => f.InternalName == faction);
|
||||
|
||||
if (selected == null)
|
||||
@@ -116,14 +132,32 @@ namespace OpenRA
|
||||
return selected;
|
||||
}
|
||||
|
||||
static FactionInfo ChooseDisplayFaction(World world, string factionName)
|
||||
static FactionInfo ResolveFaction(World world, string factionName, MersenneTwister playerRandom, bool requireSelectable)
|
||||
{
|
||||
var factionInfos = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>();
|
||||
return ResolveFaction(factionName, factionInfos, playerRandom, requireSelectable);
|
||||
}
|
||||
|
||||
static FactionInfo ResolveDisplayFaction(World world, string factionName)
|
||||
{
|
||||
var factions = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>().ToArray();
|
||||
|
||||
return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First();
|
||||
}
|
||||
|
||||
public Player(World world, Session.Client client, PlayerReference pr)
|
||||
public static string ResolvePlayerName(Session.Client client, IEnumerable<Session.Client> clients, IEnumerable<IBotInfo> botInfos)
|
||||
{
|
||||
if (client.Bot != null)
|
||||
{
|
||||
var botInfo = botInfos.First(b => b.Type == client.Bot);
|
||||
var botsOfSameType = clients.Where(c => c.Bot == client.Bot).ToArray();
|
||||
return botsOfSameType.Length == 1 ? botInfo.Name : "{0} {1}".F(botInfo.Name, botsOfSameType.IndexOf(client) + 1);
|
||||
}
|
||||
|
||||
return client.Name;
|
||||
}
|
||||
|
||||
public Player(World world, Session.Client client, PlayerReference pr, MersenneTwister playerRandom)
|
||||
{
|
||||
World = world;
|
||||
InternalName = pr.Name;
|
||||
@@ -136,18 +170,16 @@ namespace OpenRA
|
||||
{
|
||||
ClientIndex = client.Index;
|
||||
Color = client.Color;
|
||||
if (client.Bot != null)
|
||||
{
|
||||
var botInfo = world.Map.Rules.Actors["player"].TraitInfos<IBotInfo>().First(b => b.Type == client.Bot);
|
||||
var botsOfSameType = world.LobbyInfo.Clients.Where(c => c.Bot == client.Bot).ToArray();
|
||||
PlayerName = botsOfSameType.Length == 1 ? botInfo.Name : "{0} {1}".F(botInfo.Name, botsOfSameType.IndexOf(client) + 1);
|
||||
}
|
||||
else
|
||||
PlayerName = client.Name;
|
||||
PlayerName = ResolvePlayerName(client, world.LobbyInfo.Clients, world.Map.Rules.Actors["player"].TraitInfos<IBotInfo>());
|
||||
|
||||
BotType = client.Bot;
|
||||
Faction = ChooseFaction(world, client.Faction, !pr.LockFaction);
|
||||
DisplayFaction = ChooseDisplayFaction(world, client.Faction);
|
||||
Faction = ResolveFaction(world, client.Faction, playerRandom, !pr.LockFaction);
|
||||
DisplayFaction = ResolveDisplayFaction(world, client.Faction);
|
||||
|
||||
var assignSpawnPoints = world.WorldActor.TraitOrDefault<IAssignSpawnPoints>();
|
||||
HomeLocation = assignSpawnPoints?.AssignHomeLocation(world, client, playerRandom) ?? pr.HomeLocation;
|
||||
SpawnPoint = assignSpawnPoints?.SpawnPointForPlayer(this) ?? client.SpawnPoint;
|
||||
DisplaySpawnPoint = client.SpawnPoint;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -157,13 +189,15 @@ namespace OpenRA
|
||||
PlayerName = pr.Name;
|
||||
NonCombatant = pr.NonCombatant;
|
||||
Playable = pr.Playable;
|
||||
Spectating = pr.Spectating;
|
||||
spectating = pr.Spectating;
|
||||
BotType = pr.Bot;
|
||||
Faction = ChooseFaction(world, pr.Faction, false);
|
||||
DisplayFaction = ChooseDisplayFaction(world, pr.Faction);
|
||||
Faction = ResolveFaction(world, pr.Faction, playerRandom, false);
|
||||
DisplayFaction = ResolveDisplayFaction(world, pr.Faction);
|
||||
HomeLocation = pr.HomeLocation;
|
||||
SpawnPoint = DisplaySpawnPoint = 0;
|
||||
}
|
||||
|
||||
if (!Spectating)
|
||||
if (!spectating)
|
||||
PlayerMask = new LongBitSet<PlayerBitMask>(InternalName);
|
||||
|
||||
// Set this property before running any Created callbacks on the player actor
|
||||
@@ -204,11 +238,27 @@ namespace OpenRA
|
||||
return "{0} ({1})".F(PlayerName, ClientIndex);
|
||||
}
|
||||
|
||||
public Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
|
||||
public PlayerRelationship RelationshipWith(Player other)
|
||||
{
|
||||
if (this == other)
|
||||
return PlayerRelationship.Ally;
|
||||
|
||||
// Observers are considered allies to active combatants
|
||||
if (other == null || other.Spectating)
|
||||
return NonCombatant ? PlayerRelationship.Neutral : PlayerRelationship.Ally;
|
||||
|
||||
if (AlliedPlayersMask.Overlaps(other.PlayerMask))
|
||||
return PlayerRelationship.Ally;
|
||||
|
||||
if (EnemyPlayersMask.Overlaps(other.PlayerMask))
|
||||
return PlayerRelationship.Enemy;
|
||||
|
||||
return PlayerRelationship.Neutral;
|
||||
}
|
||||
|
||||
public bool IsAlliedWith(Player p)
|
||||
{
|
||||
// Observers are considered allies to active combatants
|
||||
return p == null || Stances[p] == Stance.Ally || (p.Spectating && !NonCombatant);
|
||||
return RelationshipWith(p) == PlayerRelationship.Ally;
|
||||
}
|
||||
|
||||
public Color PlayerStanceColor(Actor a)
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace OpenRA.Primitives
|
||||
static class LongBitSetAllocator<T> where T : class
|
||||
{
|
||||
static readonly Cache<string, long> Bits = new Cache<string, long>(Allocate);
|
||||
static long allBits = 1;
|
||||
static long nextBits = 1;
|
||||
|
||||
static long Allocate(string value)
|
||||
@@ -25,6 +26,7 @@ namespace OpenRA.Primitives
|
||||
lock (Bits)
|
||||
{
|
||||
var bits = nextBits;
|
||||
allBits |= bits;
|
||||
nextBits <<= 1;
|
||||
|
||||
if (nextBits == 0)
|
||||
@@ -85,6 +87,8 @@ namespace OpenRA.Primitives
|
||||
nextBits = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static long Mask { get { return allBits; } }
|
||||
}
|
||||
|
||||
// Opitmized BitSet to be used only when guaranteed to be no more than 64 values.
|
||||
@@ -114,6 +118,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public static bool operator ==(LongBitSet<T> me, LongBitSet<T> other) { return me.bits == other.bits; }
|
||||
public static bool operator !=(LongBitSet<T> me, LongBitSet<T> other) { return !(me == other); }
|
||||
public static LongBitSet<T> operator ~(LongBitSet<T> me) { return new LongBitSet<T>(me.bits ^ LongBitSetAllocator<T>.Mask); }
|
||||
|
||||
public bool Equals(LongBitSet<T> other) { return other == this; }
|
||||
public override bool Equals(object obj) { return obj is LongBitSet<T> && Equals((LongBitSet<T>)obj); }
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace OpenRA.Primitives
|
||||
@@ -18,7 +19,7 @@ namespace OpenRA.Primitives
|
||||
public Stream Stream1 { get; set; }
|
||||
public Stream Stream2 { get; set; }
|
||||
|
||||
long VirtualLength { get; set; }
|
||||
long VirtualLength { get; }
|
||||
long position;
|
||||
|
||||
public MergedStream(Stream stream1, Stream stream2)
|
||||
@@ -61,7 +62,21 @@ namespace OpenRA.Primitives
|
||||
|
||||
public override void SetLength(long value)
|
||||
{
|
||||
VirtualLength = value;
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override int ReadByte()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (position >= Stream1.Length)
|
||||
value = Stream2.ReadByte();
|
||||
else
|
||||
value = Stream1.ReadByte();
|
||||
|
||||
position++;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
@@ -83,12 +98,14 @@ namespace OpenRA.Primitives
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (position >= Stream1.Length)
|
||||
Stream2.Write(buffer, offset - (int)Stream1.Length, count);
|
||||
else
|
||||
Stream1.Write(buffer, offset, count);
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override bool CanRead
|
||||
@@ -103,7 +120,7 @@ namespace OpenRA.Primitives
|
||||
|
||||
public override bool CanWrite
|
||||
{
|
||||
get { return Stream1.CanWrite && Stream2.CanWrite; }
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override long Length
|
||||
|
||||
@@ -48,9 +48,25 @@ namespace OpenRA.Primitives
|
||||
|
||||
public sealed override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); }
|
||||
public sealed override void SetLength(long value) { throw new NotSupportedException(); }
|
||||
public sealed override void WriteByte(byte value) { throw new NotSupportedException(); }
|
||||
public sealed override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); }
|
||||
public sealed override void Flush() { throw new NotSupportedException(); }
|
||||
|
||||
public sealed override int ReadByte()
|
||||
{
|
||||
if (data.Count > 0)
|
||||
return data.Dequeue();
|
||||
|
||||
while (!baseStreamEmpty)
|
||||
{
|
||||
baseStreamEmpty = BufferData(baseStream, data);
|
||||
if (data.Count > 0)
|
||||
return data.Dequeue();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public sealed override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var copied = 0;
|
||||
@@ -66,7 +82,8 @@ namespace OpenRA.Primitives
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data into a buffer, which will be used to satisfy <see cref="Read(byte[], int, int)"/> calls.
|
||||
/// Reads data into a buffer, which will be used to satisfy <see cref="ReadByte()"/> and
|
||||
/// <see cref="Read(byte[], int, int)"/> calls.
|
||||
/// </summary>
|
||||
/// <param name="baseStream">The source stream from which bytes should be read.</param>
|
||||
/// <param name="data">The queue where bytes should be enqueued. Do not dequeue from this buffer.</param>
|
||||
|
||||
@@ -59,8 +59,35 @@ namespace OpenRA.Primitives
|
||||
set { BaseStream.Position = BaseOffset + value; }
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) { return BaseStream.Read(buffer, offset, count); }
|
||||
public override void Write(byte[] buffer, int offset, int count) { BaseStream.Write(buffer, offset, count); }
|
||||
public override int ReadByte()
|
||||
{
|
||||
if (Position < Length)
|
||||
return BaseStream.ReadByte();
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var remaining = Length - Position;
|
||||
if (remaining <= 0)
|
||||
return 0;
|
||||
return BaseStream.Read(buffer, offset, (int)Math.Min(remaining, count));
|
||||
}
|
||||
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
if (Position < Length)
|
||||
BaseStream.WriteByte(value);
|
||||
throw new IOException("Attempted to write past the end of the stream.");
|
||||
}
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if (Position + count >= Length)
|
||||
throw new IOException("Attempted to write past the end of the stream.");
|
||||
BaseStream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
public override void Flush() { BaseStream.Flush(); }
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace OpenRA
|
||||
{
|
||||
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Mimic a built-in type alias.")]
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct float3 : IEquatable<float3>
|
||||
public readonly struct float3 : IEquatable<float3>
|
||||
{
|
||||
public readonly float X, Y, Z;
|
||||
public float2 XY { get { return new float2(X, Y); } }
|
||||
@@ -28,13 +28,13 @@ namespace OpenRA
|
||||
public static implicit operator float3(int2 src) { return new float3(src.X, src.Y, 0); }
|
||||
public static implicit operator float3(float2 src) { return new float3(src.X, src.Y, 0); }
|
||||
|
||||
public static float3 operator +(float3 a, float3 b) { return new float3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); }
|
||||
public static float3 operator -(float3 a, float3 b) { return new float3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); }
|
||||
public static float3 operator -(float3 a) { return new float3(-a.X, -a.Y, -a.Z); }
|
||||
public static float3 operator *(float3 a, float3 b) { return new float3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); }
|
||||
public static float3 operator *(float a, float3 b) { return new float3(a * b.X, a * b.Y, a * b.Z); }
|
||||
public static float3 operator /(float3 a, float3 b) { return new float3(a.X / b.X, a.Y / b.Y, a.Z / b.Z); }
|
||||
public static float3 operator /(float3 a, float b) { return new float3(a.X / b, a.Y / b, a.Z / b); }
|
||||
public static float3 operator +(in float3 a, in float3 b) { return new float3(a.X + b.X, a.Y + b.Y, a.Z + b.Z); }
|
||||
public static float3 operator -(in float3 a, in float3 b) { return new float3(a.X - b.X, a.Y - b.Y, a.Z - b.Z); }
|
||||
public static float3 operator -(in float3 a) { return new float3(-a.X, -a.Y, -a.Z); }
|
||||
public static float3 operator *(in float3 a, in float3 b) { return new float3(a.X * b.X, a.Y * b.Y, a.Z * b.Z); }
|
||||
public static float3 operator *(float a, in float3 b) { return new float3(a * b.X, a * b.Y, a * b.Z); }
|
||||
public static float3 operator /(in float3 a, in float3 b) { return new float3(a.X / b.X, a.Y / b.Y, a.Z / b.Z); }
|
||||
public static float3 operator /(in float3 a, float b) { return new float3(a.X / b, a.Y / b, a.Z / b); }
|
||||
|
||||
public static float3 Lerp(float3 a, float3 b, float t)
|
||||
{
|
||||
@@ -44,8 +44,8 @@ namespace OpenRA
|
||||
float2.Lerp(a.Z, b.Z, t));
|
||||
}
|
||||
|
||||
public static bool operator ==(float3 me, float3 other) { return me.X == other.X && me.Y == other.Y && me.Z == other.Z; }
|
||||
public static bool operator !=(float3 me, float3 other) { return !(me == other); }
|
||||
public static bool operator ==(in float3 me, in float3 other) { return me.X == other.X && me.Y == other.Y && me.Z == other.Z; }
|
||||
public static bool operator !=(in float3 me, in float3 other) { return !(me == other); }
|
||||
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); }
|
||||
|
||||
public bool Equals(float3 other)
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace OpenRA
|
||||
|
||||
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height),
|
||||
graphicSettings.Mode, graphicSettings.UIScale, graphicSettings.BatchSize,
|
||||
graphicSettings.VideoDisplay, graphicSettings.GLProfile);
|
||||
graphicSettings.VideoDisplay, graphicSettings.GLProfile, !graphicSettings.DisableLegacyGL);
|
||||
|
||||
Context = Window.Context;
|
||||
|
||||
|
||||
@@ -161,8 +161,8 @@ namespace OpenRA.Scripting
|
||||
.ToArray();
|
||||
PlayerCommands = FilterCommands(world.Map.Rules.Actors["player"], knownPlayerCommands);
|
||||
|
||||
runtime.Globals["GameDir"] = Platform.GameDir;
|
||||
runtime.DoBuffer(File.Open(Platform.ResolvePath(".", "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();
|
||||
runtime.Globals["EngineDir"] = Platform.EngineDir;
|
||||
runtime.DoBuffer(File.Open(Path.Combine(Platform.EngineDir, "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();
|
||||
tick = (LuaFunction)runtime.Globals["Tick"];
|
||||
|
||||
// Register globals
|
||||
|
||||
@@ -86,9 +86,11 @@ namespace OpenRA.Scripting
|
||||
{
|
||||
foreach (var arg in clrArgs)
|
||||
{
|
||||
if (!(arg is LuaValue[]))
|
||||
var table = arg as LuaValue[];
|
||||
if (table == null)
|
||||
continue;
|
||||
foreach (var value in (LuaValue[])arg)
|
||||
|
||||
foreach (var value in table)
|
||||
value.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace OpenRA.Traits
|
||||
if (a.Owner == viewer || viewer == null)
|
||||
return basePriority;
|
||||
|
||||
switch (viewer.Stances[a.Owner])
|
||||
switch (viewer.RelationshipWith(a.Owner))
|
||||
{
|
||||
case Stance.Ally: return basePriority - PriorityRange;
|
||||
case Stance.Neutral: return basePriority - 2 * PriorityRange;
|
||||
case Stance.Enemy: return basePriority - 3 * PriorityRange;
|
||||
case PlayerRelationship.Ally: return basePriority - PriorityRange;
|
||||
case PlayerRelationship.Neutral: return basePriority - 2 * PriorityRange;
|
||||
case PlayerRelationship.Enemy: return basePriority - 3 * PriorityRange;
|
||||
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace OpenRA.Server
|
||||
// Order types are:
|
||||
// - 0x65: World sync hash:
|
||||
// - Int32 containing the sync hash value
|
||||
// - UInt64 containing the current defeat state (a bit set
|
||||
// to 1 means the corresponding player is defeated)
|
||||
// - 0xBF: Player disconnected
|
||||
// - 0xFE: Handshake (also used for ServerOrders for ProtocolVersion.Orders < 8)
|
||||
// - Length-prefixed string specifying a name or key
|
||||
@@ -66,6 +68,6 @@ namespace OpenRA.Server
|
||||
// The protocol for server and world orders
|
||||
// This applies after the handshake has completed, and is provided to support
|
||||
// alternative server implementations that wish to support multiple versions in parallel
|
||||
public const int Orders = 10;
|
||||
public const int Orders = 11;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -88,6 +88,9 @@ namespace OpenRA
|
||||
[Desc("Allow clients to see the country of other clients.")]
|
||||
public bool EnableGeoIP = true;
|
||||
|
||||
[Desc("For dedicated servers only, save replays for all games played.")]
|
||||
public bool RecordReplays = false;
|
||||
|
||||
public ServerSettings Clone()
|
||||
{
|
||||
return (ServerSettings)MemberwiseClone();
|
||||
@@ -172,14 +175,18 @@ namespace OpenRA
|
||||
[Desc("Disable operating-system provided cursor rendering.")]
|
||||
public bool DisableHardwareCursors = false;
|
||||
|
||||
[Desc("Disable legacy OpenGL 2.1 support.")]
|
||||
public bool DisableLegacyGL = true;
|
||||
|
||||
[Desc("Display index to use in a multi-monitor fullscreen setup.")]
|
||||
public int VideoDisplay = 0;
|
||||
|
||||
[Desc("Preferred OpenGL profile to use.",
|
||||
"Modern: OpenGL Core Profile 3.2 or greater.",
|
||||
"Embedded: OpenGL ES 3.0 or greater.",
|
||||
"Legacy: OpenGL 2.1 with framebuffer_object extension.")]
|
||||
public GLProfile GLProfile = GLProfile.Modern;
|
||||
"Legacy: OpenGL 2.1 with framebuffer_object extension (requires DisableLegacyGL: False)",
|
||||
"Automatic: Use the first supported profile.")]
|
||||
public GLProfile GLProfile = GLProfile.Automatic;
|
||||
|
||||
public int BatchSize = 8192;
|
||||
public int SheetSize = 2048;
|
||||
|
||||
@@ -61,22 +61,22 @@ namespace OpenRA
|
||||
|
||||
public static ushort ReadUInt16(this Stream s)
|
||||
{
|
||||
return BitConverter.ToUInt16(s.ReadBytes(2), 0);
|
||||
return (ushort)(s.ReadUInt8() | s.ReadUInt8() << 8);
|
||||
}
|
||||
|
||||
public static short ReadInt16(this Stream s)
|
||||
{
|
||||
return BitConverter.ToInt16(s.ReadBytes(2), 0);
|
||||
return (short)(s.ReadUInt8() | s.ReadUInt8() << 8);
|
||||
}
|
||||
|
||||
public static uint ReadUInt32(this Stream s)
|
||||
{
|
||||
return BitConverter.ToUInt32(s.ReadBytes(4), 0);
|
||||
return (uint)(s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24);
|
||||
}
|
||||
|
||||
public static int ReadInt32(this Stream s)
|
||||
{
|
||||
return BitConverter.ToInt32(s.ReadBytes(4), 0);
|
||||
return s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24;
|
||||
}
|
||||
|
||||
public static void Write(this Stream s, int value)
|
||||
|
||||
362
OpenRA.Game/Support/AssemblyLoader.cs
Normal file
362
OpenRA.Game/Support/AssemblyLoader.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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
|
||||
|
||||
// Not used/usable on Mono. Only used for Dotnet Core. Based on https://github.com/natemcmaster/DotNetCorePlugins
|
||||
#if !MONO
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Loader;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
|
||||
namespace OpenRA.Support
|
||||
{
|
||||
public class AssemblyLoader
|
||||
{
|
||||
readonly string mainAssembly;
|
||||
readonly AssemblyLoadContext context;
|
||||
|
||||
public Assembly LoadDefaultAssembly() => context.LoadFromAssemblyPath(mainAssembly);
|
||||
|
||||
public AssemblyLoader(string assemblyFile)
|
||||
{
|
||||
mainAssembly = assemblyFile;
|
||||
var baseDir = Path.GetDirectoryName(assemblyFile);
|
||||
|
||||
context = CreateLoadContext(baseDir, assemblyFile);
|
||||
}
|
||||
|
||||
static AssemblyLoadContext CreateLoadContext(string baseDir, string assemblyFile)
|
||||
{
|
||||
var depsJsonFile = Path.Combine(baseDir, Path.GetFileNameWithoutExtension(assemblyFile) + ".deps.json");
|
||||
|
||||
var builder = new AssemblyLoadContextBuilder();
|
||||
|
||||
builder.TryAddDependencyContext(depsJsonFile, out _);
|
||||
builder.SetBaseDirectory(baseDir);
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
||||
public class AssemblyLoadContextBuilder
|
||||
{
|
||||
readonly Dictionary<string, ManagedLibrary> managedLibraries = new Dictionary<string, ManagedLibrary>(StringComparer.Ordinal);
|
||||
readonly Dictionary<string, NativeLibrary> nativeLibraries = new Dictionary<string, NativeLibrary>(StringComparer.Ordinal);
|
||||
string basePath;
|
||||
|
||||
public AssemblyLoadContext Build()
|
||||
{
|
||||
return new ManagedLoadContext(basePath, managedLibraries, nativeLibraries);
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder SetBaseDirectory(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException("Argument must not be null or empty.", nameof(path));
|
||||
|
||||
if (!Path.IsPathRooted(path))
|
||||
throw new ArgumentException("Argument must be a full path.", nameof(path));
|
||||
|
||||
basePath = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder AddManagedLibrary(ManagedLibrary library)
|
||||
{
|
||||
managedLibraries.Add(library.Name.Name, library);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssemblyLoadContextBuilder AddNativeLibrary(NativeLibrary library)
|
||||
{
|
||||
ValidateRelativePath(library.AppLocalPath);
|
||||
nativeLibraries.Add(library.Name, library);
|
||||
return this;
|
||||
}
|
||||
|
||||
static void ValidateRelativePath(string probingPath)
|
||||
{
|
||||
if (string.IsNullOrEmpty(probingPath))
|
||||
throw new ArgumentException("Value must not be null or empty.", nameof(probingPath));
|
||||
|
||||
if (Path.IsPathRooted(probingPath))
|
||||
throw new ArgumentException("Argument must be a relative path.", nameof(probingPath));
|
||||
}
|
||||
}
|
||||
|
||||
class ManagedLoadContext : AssemblyLoadContext
|
||||
{
|
||||
readonly string basePath;
|
||||
readonly Dictionary<string, ManagedLibrary> managedAssemblies;
|
||||
readonly Dictionary<string, NativeLibrary> nativeLibraries;
|
||||
|
||||
static readonly string[] NativeLibraryExtensions;
|
||||
static readonly string[] NativeLibraryPrefixes;
|
||||
|
||||
static readonly string[] ManagedAssemblyExtensions =
|
||||
{
|
||||
".dll",
|
||||
".ni.dll",
|
||||
".exe",
|
||||
".ni.exe"
|
||||
};
|
||||
|
||||
static ManagedLoadContext()
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "" };
|
||||
NativeLibraryExtensions = new[] { ".dll" };
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "", "lib", };
|
||||
NativeLibraryExtensions = new[] { ".dylib" };
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
NativeLibraryPrefixes = new[] { "", "lib" };
|
||||
NativeLibraryExtensions = new[] { ".so", ".so.1" };
|
||||
}
|
||||
else
|
||||
{
|
||||
NativeLibraryPrefixes = Array.Empty<string>();
|
||||
NativeLibraryExtensions = Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
|
||||
public ManagedLoadContext(string baseDirectory, Dictionary<string, ManagedLibrary> managedAssemblies, Dictionary<string, NativeLibrary> nativeLibraries)
|
||||
{
|
||||
basePath = baseDirectory ?? throw new ArgumentNullException(nameof(baseDirectory));
|
||||
this.managedAssemblies = managedAssemblies ?? throw new ArgumentNullException(nameof(managedAssemblies));
|
||||
this.nativeLibraries = nativeLibraries ?? throw new ArgumentNullException(nameof(nativeLibraries));
|
||||
}
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
// If default context is preferred, check first for types in the default context unless the dependency has been declared as private
|
||||
try
|
||||
{
|
||||
var defaultAssembly = Default.LoadFromAssemblyName(assemblyName);
|
||||
if (defaultAssembly != null)
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Swallow errors in loading from the default context
|
||||
}
|
||||
|
||||
if (managedAssemblies.TryGetValue(assemblyName.Name, out var library) && SearchForLibrary(library, out var path))
|
||||
return LoadFromAssemblyPath(path);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
|
||||
{
|
||||
foreach (var prefix in NativeLibraryPrefixes)
|
||||
if (nativeLibraries.TryGetValue(prefix + unmanagedDllName, out var library) && SearchForLibrary(library, prefix, out var path))
|
||||
return LoadUnmanagedDllFromPath(path);
|
||||
|
||||
return base.LoadUnmanagedDll(unmanagedDllName);
|
||||
}
|
||||
|
||||
bool SearchForLibrary(ManagedLibrary library, out string path)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in ManagedAssemblyExtensions)
|
||||
{
|
||||
var local = Path.Combine(basePath, library.Name.Name + ext);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SearchForLibrary(NativeLibrary library, string prefix, out string path)
|
||||
{
|
||||
// 1. Search in base path
|
||||
foreach (var ext in NativeLibraryExtensions)
|
||||
{
|
||||
var candidate = Path.Combine(basePath, $"{prefix}{library.Name}{ext}");
|
||||
if (File.Exists(candidate))
|
||||
{
|
||||
path = candidate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Search in base path + app local (for portable deployments of netcoreapp)
|
||||
var local = Path.Combine(basePath, library.AppLocalPath);
|
||||
if (File.Exists(local))
|
||||
{
|
||||
path = local;
|
||||
return true;
|
||||
}
|
||||
|
||||
path = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class ManagedLibrary
|
||||
{
|
||||
public AssemblyName Name { get; private set; }
|
||||
|
||||
public static ManagedLibrary CreateFromPackage(string assetPath)
|
||||
{
|
||||
return new ManagedLibrary
|
||||
{
|
||||
Name = new AssemblyName(Path.GetFileNameWithoutExtension(assetPath))
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public class NativeLibrary
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
|
||||
public string AppLocalPath { get; private set; }
|
||||
|
||||
public static NativeLibrary CreateFromPackage(string assetPath)
|
||||
{
|
||||
return new NativeLibrary
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(assetPath),
|
||||
AppLocalPath = assetPath
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class DependencyContextExtensions
|
||||
{
|
||||
public static AssemblyLoadContextBuilder TryAddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath, out Exception error)
|
||||
{
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
builder.AddDependencyContext(depsFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, string depsFilePath)
|
||||
{
|
||||
var reader = new DependencyContextJsonReader();
|
||||
using (var file = File.OpenRead(depsFilePath))
|
||||
{
|
||||
var deps = reader.Read(file);
|
||||
builder.SetBaseDirectory(Path.GetDirectoryName(depsFilePath));
|
||||
builder.AddDependencyContext(deps);
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
static string GetFallbackRid()
|
||||
{
|
||||
string ridBase;
|
||||
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
ridBase = "win10";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
ridBase = "linux";
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
ridBase = "osx.10.12";
|
||||
else
|
||||
return "any";
|
||||
|
||||
switch (RuntimeInformation.OSArchitecture)
|
||||
{
|
||||
case Architecture.X86:
|
||||
return ridBase + "-x86";
|
||||
case Architecture.X64:
|
||||
return ridBase + "-x64";
|
||||
case Architecture.Arm:
|
||||
return ridBase + "-arm";
|
||||
case Architecture.Arm64:
|
||||
return ridBase + "-arm64";
|
||||
}
|
||||
|
||||
return ridBase;
|
||||
}
|
||||
|
||||
public static AssemblyLoadContextBuilder AddDependencyContext(this AssemblyLoadContextBuilder builder, DependencyContext dependencyContext)
|
||||
{
|
||||
var ridGraph = dependencyContext.RuntimeGraph.Any()
|
||||
? dependencyContext.RuntimeGraph
|
||||
: DependencyContext.Default.RuntimeGraph;
|
||||
|
||||
var rid = Microsoft.DotNet.PlatformAbstractions.RuntimeEnvironment.GetRuntimeIdentifier();
|
||||
var fallbackRid = GetFallbackRid();
|
||||
var fallbackGraph = ridGraph.FirstOrDefault(g => g.Runtime == rid)
|
||||
?? ridGraph.FirstOrDefault(g => g.Runtime == fallbackRid)
|
||||
?? new RuntimeFallbacks("any");
|
||||
|
||||
foreach (var managed in dependencyContext.ResolveRuntimeAssemblies(fallbackGraph))
|
||||
builder.AddManagedLibrary(managed);
|
||||
|
||||
foreach (var native in dependencyContext.ResolveNativeAssets(fallbackGraph))
|
||||
builder.AddNativeLibrary(native);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
static IEnumerable<ManagedLibrary> ResolveRuntimeAssemblies(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
var rids = GetRids(runtimeGraph);
|
||||
return from library in depContext.RuntimeLibraries
|
||||
from assetPath in SelectAssets(rids, library.RuntimeAssemblyGroups)
|
||||
select ManagedLibrary.CreateFromPackage(assetPath);
|
||||
}
|
||||
|
||||
static IEnumerable<NativeLibrary> ResolveNativeAssets(this DependencyContext depContext, RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
var rids = GetRids(runtimeGraph);
|
||||
return from library in depContext.RuntimeLibraries
|
||||
from assetPath in SelectAssets(rids, library.NativeLibraryGroups)
|
||||
where !assetPath.EndsWith(".a", StringComparison.Ordinal)
|
||||
select NativeLibrary.CreateFromPackage(assetPath);
|
||||
}
|
||||
|
||||
static IEnumerable<string> GetRids(RuntimeFallbacks runtimeGraph)
|
||||
{
|
||||
return Enumerable.Concat(new[] { runtimeGraph.Runtime }, runtimeGraph?.Fallbacks ?? Enumerable.Empty<string>());
|
||||
}
|
||||
|
||||
static IEnumerable<string> SelectAssets(IEnumerable<string> rids, IEnumerable<RuntimeAssetGroup> groups)
|
||||
{
|
||||
foreach (var rid in rids)
|
||||
{
|
||||
var group = groups.FirstOrDefault(g => g.Runtime == rid);
|
||||
if (group != null)
|
||||
return group.AssetPaths;
|
||||
}
|
||||
|
||||
return groups.GetDefaultAssets();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -21,26 +21,4 @@ namespace OpenRA
|
||||
Success = 0,
|
||||
Running = int.MaxValue
|
||||
}
|
||||
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static int Main(string[] args)
|
||||
{
|
||||
if (Debugger.IsAttached || args.Contains("--just-die"))
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
|
||||
|
||||
try
|
||||
{
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionHandler.HandleFatalError(e);
|
||||
return (int)RunStatus.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA
|
||||
{
|
||||
@@ -122,6 +124,16 @@ namespace OpenRA
|
||||
t.Value.RemoveActor(a.ActorID);
|
||||
}
|
||||
|
||||
public void ApplyToActorsWithTrait<T>(Action<Actor, T> action)
|
||||
{
|
||||
InnerGet<T>().ApplyToAll(action);
|
||||
}
|
||||
|
||||
public void ApplyToActorsWithTraitTimed<T>(Action<Actor, T> action, string text)
|
||||
{
|
||||
InnerGet<T>().ApplyToAllTimed(action, text);
|
||||
}
|
||||
|
||||
interface ITraitContainer
|
||||
{
|
||||
void Add(Actor actor, object trait);
|
||||
@@ -277,6 +289,32 @@ namespace OpenRA
|
||||
actors.RemoveRange(startIndex, count);
|
||||
traits.RemoveRange(startIndex, count);
|
||||
}
|
||||
|
||||
public void ApplyToAll(Action<Actor, T> action)
|
||||
{
|
||||
for (var i = 0; i < actors.Count; i++)
|
||||
action(actors[i], traits[i]);
|
||||
}
|
||||
|
||||
public void ApplyToAllTimed(Action<Actor, T> action, string text)
|
||||
{
|
||||
var longTickThresholdInStopwatchTicks = PerfTimer.LongTickThresholdInStopwatchTicks;
|
||||
var start = Stopwatch.GetTimestamp();
|
||||
for (var i = 0; i < actors.Count; i++)
|
||||
{
|
||||
var actor = actors[i];
|
||||
var trait = traits[i];
|
||||
action(actor, trait);
|
||||
var current = Stopwatch.GetTimestamp();
|
||||
if (current - start > longTickThresholdInStopwatchTicks)
|
||||
{
|
||||
PerfTimer.LogLongTick(start, current, text, trait);
|
||||
start = Stopwatch.GetTimestamp();
|
||||
}
|
||||
else
|
||||
start = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,54 +13,53 @@ using System;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
/* attributes used by OpenRA.Lint to understand the rules */
|
||||
public enum LintDictionaryReference
|
||||
{
|
||||
None = 0,
|
||||
Keys = 1,
|
||||
Values = 2
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class ActorReferenceAttribute : Attribute
|
||||
{
|
||||
public Type[] RequiredTraits;
|
||||
public ActorReferenceAttribute(params Type[] requiredTraits)
|
||||
public readonly Type[] RequiredTraits;
|
||||
public readonly LintDictionaryReference DictionaryReference;
|
||||
|
||||
public ActorReferenceAttribute(Type[] requiredTraits,
|
||||
LintDictionaryReference dictionaryReference = LintDictionaryReference.None)
|
||||
{
|
||||
RequiredTraits = requiredTraits;
|
||||
DictionaryReference = dictionaryReference;
|
||||
}
|
||||
|
||||
public ActorReferenceAttribute(Type requiredTrait = null,
|
||||
LintDictionaryReference dictionaryReference = LintDictionaryReference.None)
|
||||
{
|
||||
RequiredTraits = requiredTrait != null ? new[] { requiredTrait } : new Type[0];
|
||||
DictionaryReference = dictionaryReference;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class WeaponReferenceAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class VoiceSetReferenceAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class VoiceReferenceAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class LocomotorReferenceAttribute : Attribute { }
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class NotificationReferenceAttribute : Attribute
|
||||
{
|
||||
public readonly string NotificationTypeFieldName = null;
|
||||
public readonly string NotificationType = null;
|
||||
|
||||
public NotificationReferenceAttribute(string type = null, string typeFromField = null)
|
||||
{
|
||||
NotificationType = type;
|
||||
NotificationTypeFieldName = typeFromField;
|
||||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public sealed class SequenceReferenceAttribute : Attribute
|
||||
{
|
||||
public readonly string ImageReference; // The field name in the same trait info that contains the image name.
|
||||
// The field name in the same trait info that contains the image name.
|
||||
public readonly string ImageReference;
|
||||
public readonly bool Prefix;
|
||||
public readonly bool ActorNameFallback;
|
||||
public SequenceReferenceAttribute(string imageReference = null, bool prefix = false, bool actorNameFallback = false)
|
||||
public readonly bool AllowNullImage;
|
||||
public readonly LintDictionaryReference DictionaryReference;
|
||||
|
||||
public SequenceReferenceAttribute(string imageReference = null, bool prefix = false, bool allowNullImage = false,
|
||||
LintDictionaryReference dictionaryReference = LintDictionaryReference.None)
|
||||
{
|
||||
ImageReference = imageReference;
|
||||
Prefix = prefix;
|
||||
ActorNameFallback = actorNameFallback;
|
||||
AllowNullImage = allowNullImage;
|
||||
DictionaryReference = dictionaryReference;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,10 +39,11 @@ namespace OpenRA.Traits
|
||||
readonly ICreatesFrozenActors frozenTrait;
|
||||
readonly Player viewer;
|
||||
readonly Shroud shroud;
|
||||
readonly List<WPos> targetablePositions = new List<WPos>();
|
||||
|
||||
public Player Owner { get; private set; }
|
||||
public BitSet<TargetableType> TargetTypes { get; private set; }
|
||||
public WPos[] TargetablePositions { get; private set; }
|
||||
public IEnumerable<WPos> TargetablePositions { get { return targetablePositions; } }
|
||||
|
||||
public ITooltipInfo TooltipInfo { get; private set; }
|
||||
public Player TooltipOwner { get; private set; }
|
||||
@@ -117,7 +118,8 @@ namespace OpenRA.Traits
|
||||
{
|
||||
Owner = actor.Owner;
|
||||
TargetTypes = actor.GetEnabledTargetTypes();
|
||||
TargetablePositions = actor.GetTargetablePositions().ToArray();
|
||||
targetablePositions.Clear();
|
||||
targetablePositions.AddRange(actor.GetTargetablePositions());
|
||||
Hidden = !actor.CanBeViewedByPlayer(viewer);
|
||||
|
||||
if (health != null)
|
||||
|
||||
@@ -99,6 +99,7 @@ namespace OpenRA.Traits
|
||||
readonly ProjectedCellLayer<short> generatedShroudCount;
|
||||
readonly ProjectedCellLayer<bool> explored;
|
||||
readonly ProjectedCellLayer<bool> touched;
|
||||
bool anyCellTouched;
|
||||
|
||||
// Per-cell cache of the resolved cell type (shroud/fog/visible)
|
||||
readonly ProjectedCellLayer<ShroudCellType> resolvedType;
|
||||
@@ -142,6 +143,7 @@ namespace OpenRA.Traits
|
||||
generatedShroudCount = new ProjectedCellLayer<short>(map);
|
||||
explored = new ProjectedCellLayer<bool>(map);
|
||||
touched = new ProjectedCellLayer<bool>(map);
|
||||
anyCellTouched = true;
|
||||
|
||||
// Defaults to 0 = Shroud
|
||||
resolvedType = new ProjectedCellLayer<ShroudCellType>(map);
|
||||
@@ -159,31 +161,42 @@ namespace OpenRA.Traits
|
||||
|
||||
void ITick.Tick(Actor self)
|
||||
{
|
||||
if (!anyCellTouched)
|
||||
return;
|
||||
|
||||
anyCellTouched = false;
|
||||
|
||||
if (OnShroudChanged == null)
|
||||
return;
|
||||
|
||||
foreach (var puv in map.ProjectedCells)
|
||||
{
|
||||
if (!touched[puv])
|
||||
var index = touched.Index(puv);
|
||||
if (!touched[index])
|
||||
continue;
|
||||
|
||||
touched[puv] = false;
|
||||
touched[index] = false;
|
||||
|
||||
var type = ShroudCellType.Shroud;
|
||||
|
||||
if (explored[puv] && (!shroudGenerationEnabled || generatedShroudCount[puv] == 0 || visibleCount[puv] > 0))
|
||||
if (explored[index])
|
||||
{
|
||||
var count = visibleCount[puv];
|
||||
if (passiveVisibilityEnabled)
|
||||
count += passiveVisibleCount[puv];
|
||||
var count = visibleCount[index];
|
||||
if (!shroudGenerationEnabled || count > 0 || generatedShroudCount[index] == 0)
|
||||
{
|
||||
if (passiveVisibilityEnabled)
|
||||
count += passiveVisibleCount[index];
|
||||
|
||||
type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog;
|
||||
type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog;
|
||||
}
|
||||
}
|
||||
|
||||
var oldResolvedType = resolvedType[puv];
|
||||
resolvedType[puv] = type;
|
||||
var oldResolvedType = resolvedType[index];
|
||||
if (type != oldResolvedType)
|
||||
{
|
||||
resolvedType[index] = type;
|
||||
OnShroudChanged(puv);
|
||||
}
|
||||
}
|
||||
|
||||
Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick;
|
||||
@@ -231,21 +244,23 @@ namespace OpenRA.Traits
|
||||
if (!map.Contains(puv))
|
||||
continue;
|
||||
|
||||
touched[puv] = true;
|
||||
var index = touched.Index(puv);
|
||||
touched[index] = true;
|
||||
anyCellTouched = true;
|
||||
switch (type)
|
||||
{
|
||||
case SourceType.PassiveVisibility:
|
||||
passiveVisibilityEnabled = true;
|
||||
passiveVisibleCount[puv]++;
|
||||
explored[puv] = true;
|
||||
passiveVisibleCount[index]++;
|
||||
explored[index] = true;
|
||||
break;
|
||||
case SourceType.Visibility:
|
||||
visibleCount[puv]++;
|
||||
explored[puv] = true;
|
||||
visibleCount[index]++;
|
||||
explored[index] = true;
|
||||
break;
|
||||
case SourceType.Shroud:
|
||||
shroudGenerationEnabled = true;
|
||||
generatedShroudCount[puv]++;
|
||||
generatedShroudCount[index]++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -261,17 +276,19 @@ namespace OpenRA.Traits
|
||||
// Cells outside the visible bounds don't increment visibleCount
|
||||
if (map.Contains(puv))
|
||||
{
|
||||
touched[puv] = true;
|
||||
var index = touched.Index(puv);
|
||||
touched[index] = true;
|
||||
anyCellTouched = true;
|
||||
switch (state.Type)
|
||||
{
|
||||
case SourceType.PassiveVisibility:
|
||||
passiveVisibleCount[puv]--;
|
||||
passiveVisibleCount[index]--;
|
||||
break;
|
||||
case SourceType.Visibility:
|
||||
visibleCount[puv]--;
|
||||
visibleCount[index]--;
|
||||
break;
|
||||
case SourceType.Shroud:
|
||||
generatedShroudCount[puv]--;
|
||||
generatedShroudCount[index]--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -284,10 +301,15 @@ namespace OpenRA.Traits
|
||||
{
|
||||
foreach (var puv in cells)
|
||||
{
|
||||
if (map.Contains(puv) && !explored[puv])
|
||||
if (map.Contains(puv))
|
||||
{
|
||||
touched[puv] = true;
|
||||
explored[puv] = true;
|
||||
var index = touched.Index(puv);
|
||||
if (!explored[index])
|
||||
{
|
||||
touched[index] = true;
|
||||
anyCellTouched = true;
|
||||
explored[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,10 +321,12 @@ namespace OpenRA.Traits
|
||||
|
||||
foreach (var puv in map.ProjectedCells)
|
||||
{
|
||||
if (!explored[puv] && s.explored[puv])
|
||||
var index = touched.Index(puv);
|
||||
if (!explored[index] && s.explored[index])
|
||||
{
|
||||
touched[puv] = true;
|
||||
explored[puv] = true;
|
||||
touched[index] = true;
|
||||
anyCellTouched = true;
|
||||
explored[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,10 +335,12 @@ namespace OpenRA.Traits
|
||||
{
|
||||
foreach (var puv in map.ProjectedCells)
|
||||
{
|
||||
if (!explored[puv])
|
||||
var index = touched.Index(puv);
|
||||
if (!explored[index])
|
||||
{
|
||||
touched[puv] = true;
|
||||
explored[puv] = true;
|
||||
touched[index] = true;
|
||||
anyCellTouched = true;
|
||||
explored[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -323,9 +349,12 @@ namespace OpenRA.Traits
|
||||
{
|
||||
foreach (var puv in map.ProjectedCells)
|
||||
{
|
||||
touched[puv] = true;
|
||||
explored[puv] = (visibleCount[puv] + passiveVisibleCount[puv]) > 0;
|
||||
var index = touched.Index(puv);
|
||||
touched[index] = true;
|
||||
explored[index] = (visibleCount[index] + passiveVisibleCount[index]) > 0;
|
||||
}
|
||||
|
||||
anyCellTouched = true;
|
||||
}
|
||||
|
||||
public bool IsExplored(WPos pos)
|
||||
|
||||
@@ -16,7 +16,7 @@ using System.Linq;
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
|
||||
public struct Target
|
||||
public readonly struct Target
|
||||
{
|
||||
public static readonly Target[] None = { };
|
||||
public static readonly Target Invalid = default(Target);
|
||||
@@ -83,7 +83,7 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
public static Target FromPos(WPos p) { return new Target(p); }
|
||||
public static Target FromTargetPositions(Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); }
|
||||
public static Target FromTargetPositions(in Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); }
|
||||
public static Target FromCell(World w, CPos c, SubCell subCell = SubCell.FullCell) { return new Target(w, c, subCell); }
|
||||
public static Target FromActor(Actor a) { return a != null ? new Target(a) : Invalid; }
|
||||
public static Target FromFrozenActor(FrozenActor fa) { return new Target(fa); }
|
||||
|
||||
@@ -18,6 +18,7 @@ using OpenRA.GameRules;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Network;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Support;
|
||||
|
||||
namespace OpenRA.Traits
|
||||
{
|
||||
@@ -67,7 +68,7 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Stance
|
||||
public enum PlayerRelationship
|
||||
{
|
||||
None = 0,
|
||||
Enemy = 1,
|
||||
@@ -77,7 +78,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public static class StanceExts
|
||||
{
|
||||
public static bool HasStance(this Stance s, Stance stance)
|
||||
public static bool HasStance(this PlayerRelationship s, PlayerRelationship stance)
|
||||
{
|
||||
// PERF: Enum.HasFlag is slower and requires allocations.
|
||||
return (s & stance) == stance;
|
||||
@@ -127,7 +128,7 @@ namespace OpenRA.Traits
|
||||
public interface IIssueOrder
|
||||
{
|
||||
IEnumerable<IOrderTargeter> Orders { get; }
|
||||
Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued);
|
||||
Order IssueOrder(Actor self, IOrderTargeter order, in Target target, bool queued);
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@@ -146,9 +147,9 @@ namespace OpenRA.Traits
|
||||
{
|
||||
string OrderID { get; }
|
||||
int OrderPriority { get; }
|
||||
bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor);
|
||||
bool CanTarget(Actor self, in Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor);
|
||||
bool IsQueued { get; }
|
||||
bool TargetOverridesSelection(Actor self, Target target, List<Actor> actorsAt, CPos xy, TargetModifiers modifiers);
|
||||
bool TargetOverridesSelection(Actor self, in Target target, List<Actor> actorsAt, CPos xy, TargetModifiers modifiers);
|
||||
}
|
||||
|
||||
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
|
||||
@@ -195,7 +196,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public interface ITooltipInfo : ITraitInfoInterface
|
||||
{
|
||||
string TooltipForPlayerStance(Stance stance);
|
||||
string TooltipForPlayerStance(PlayerRelationship stance);
|
||||
bool IsOwnerRowVisible { get; }
|
||||
}
|
||||
|
||||
@@ -267,6 +268,7 @@ namespace OpenRA.Traits
|
||||
public interface ISelectionDecorations
|
||||
{
|
||||
IEnumerable<IRenderable> RenderSelectionAnnotations(Actor self, WorldRenderer worldRenderer, Color color);
|
||||
int2 GetDecorationOrigin(Actor self, WorldRenderer wr, string pos, int2 margin);
|
||||
}
|
||||
|
||||
public interface IMapPreviewSignatureInfo : ITraitInfoInterface
|
||||
@@ -364,7 +366,27 @@ namespace OpenRA.Traits
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ICreatePlayers { void CreatePlayers(World w); }
|
||||
public interface ICreatePlayers { void CreatePlayers(World w, MersenneTwister playerRandom); }
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface ICreatePlayersInfo : ITraitInfoInterface
|
||||
{
|
||||
void CreateServerPlayers(MapPreview map, Session lobbyInfo, List<GameInformation.Player> players, MersenneTwister playerRandom);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IAssignSpawnPoints
|
||||
{
|
||||
CPos AssignHomeLocation(World world, Session.Client client, MersenneTwister playerRandom);
|
||||
int SpawnPointForPlayer(Player player);
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IAssignSpawnPointsInfo : ITraitInfoInterface
|
||||
{
|
||||
object InitializeState(MapPreview map, Session lobbyInfo);
|
||||
int AssignSpawnPoint(object state, Session lobbyInfo, Session.Client client, MersenneTwister playerRandom);
|
||||
}
|
||||
|
||||
public interface IBotInfo : ITraitInfoInterface
|
||||
{
|
||||
@@ -485,7 +507,10 @@ namespace OpenRA.Traits
|
||||
bool AlwaysEnabled { get; }
|
||||
}
|
||||
|
||||
public interface IMoveInfo : ITraitInfoInterface { }
|
||||
public interface IMoveInfo : ITraitInfoInterface
|
||||
{
|
||||
Color GetTargetLineColor();
|
||||
}
|
||||
|
||||
[RequireExplicitImplementation]
|
||||
public interface IGameOver { void GameOver(World world); }
|
||||
@@ -495,7 +520,7 @@ namespace OpenRA.Traits
|
||||
int Delay { get; }
|
||||
bool IsValidAgainst(Actor victim, Actor firedBy);
|
||||
bool IsValidAgainst(FrozenActor victim, Actor firedBy);
|
||||
void DoImpact(Target target, WarheadArgs args);
|
||||
void DoImpact(in Target target, WarheadArgs args);
|
||||
}
|
||||
|
||||
public interface IRulesetLoaded<TInfo> { void RulesetLoaded(Ruleset rules, TInfo info); }
|
||||
@@ -541,8 +566,8 @@ namespace OpenRA.Traits
|
||||
{
|
||||
static readonly Dictionary<string, string> BoolValues = new Dictionary<string, string>()
|
||||
{
|
||||
{ true.ToString(), "enabled" },
|
||||
{ false.ToString(), "disabled" }
|
||||
{ true.ToString(), "Enabled" },
|
||||
{ false.ToString(), "Disabled" }
|
||||
};
|
||||
|
||||
public LobbyBooleanOption(string id, string name, string description, bool visible, int displayorder, bool defaultValue, bool locked)
|
||||
@@ -550,7 +575,7 @@ namespace OpenRA.Traits
|
||||
|
||||
public override string ValueChangedMessage(string playerName, string newValue)
|
||||
{
|
||||
return playerName + " " + BoolValues[newValue] + " " + Name + ".";
|
||||
return playerName + " " + BoolValues[newValue].ToLowerInvariant() + " " + Name + ".";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ namespace OpenRA.UtilityCommands
|
||||
if (args[2] == "user" || args[2] == "both")
|
||||
type |= ModRegistration.User;
|
||||
|
||||
new ExternalMods().Register(utility.ModData.Manifest, args[1], type);
|
||||
new ExternalMods().Register(utility.ModData.Manifest, args[1], Enumerable.Empty<string>(), type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace OpenRA
|
||||
/// <summary>
|
||||
/// 3d World rotation.
|
||||
/// </summary>
|
||||
public struct WRot : IEquatable<WRot>
|
||||
public readonly struct WRot : IEquatable<WRot>
|
||||
{
|
||||
// The Euler angle representation is a lot more intuitive for public use
|
||||
public readonly WAngle Roll, Pitch, Yaw;
|
||||
@@ -84,11 +84,11 @@ namespace OpenRA
|
||||
|
||||
public static WRot FromFacing(int facing) { return new WRot(WAngle.Zero, WAngle.Zero, WAngle.FromFacing(facing)); }
|
||||
public static WRot FromYaw(WAngle yaw) { return new WRot(WAngle.Zero, WAngle.Zero, yaw); }
|
||||
public static WRot operator +(WRot a, WRot b) { return new WRot(a.Roll + b.Roll, a.Pitch + b.Pitch, a.Yaw + b.Yaw); }
|
||||
public static WRot operator -(WRot a, WRot b) { return new WRot(a.Roll - b.Roll, a.Pitch - b.Pitch, a.Yaw - b.Yaw); }
|
||||
public static WRot operator -(WRot a) { return new WRot(-a.x, -a.y, -a.z, a.w, -a.Roll, -a.Pitch, -a.Yaw); }
|
||||
public static WRot operator +(in WRot a, in WRot b) { return new WRot(a.Roll + b.Roll, a.Pitch + b.Pitch, a.Yaw + b.Yaw); }
|
||||
public static WRot operator -(in WRot a, in WRot b) { return new WRot(a.Roll - b.Roll, a.Pitch - b.Pitch, a.Yaw - b.Yaw); }
|
||||
public static WRot operator -(in WRot a) { return new WRot(-a.x, -a.y, -a.z, a.w, -a.Roll, -a.Pitch, -a.Yaw); }
|
||||
|
||||
public WRot Rotate(WRot rot)
|
||||
public WRot Rotate(in WRot rot)
|
||||
{
|
||||
if (this == None)
|
||||
return rot;
|
||||
@@ -104,12 +104,12 @@ namespace OpenRA
|
||||
return new WRot((int)rx, (int)ry, (int)rz, (int)rw);
|
||||
}
|
||||
|
||||
public static bool operator ==(WRot me, WRot other)
|
||||
public static bool operator ==(in WRot me, in WRot other)
|
||||
{
|
||||
return me.Roll == other.Roll && me.Pitch == other.Pitch && me.Yaw == other.Yaw;
|
||||
}
|
||||
|
||||
public static bool operator !=(WRot me, WRot other) { return !(me == other); }
|
||||
public static bool operator !=(in WRot me, in WRot other) { return !(me == other); }
|
||||
|
||||
public WRot WithRoll(WAngle roll)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace OpenRA
|
||||
public long VerticalLengthSquared { get { return (long)Z * Z; } }
|
||||
public int VerticalLength { get { return (int)Exts.ISqrt(VerticalLengthSquared); } }
|
||||
|
||||
public WVec Rotate(WRot rot)
|
||||
public WVec Rotate(in WRot rot)
|
||||
{
|
||||
rot.AsMatrix(out var mtx);
|
||||
return Rotate(ref mtx);
|
||||
|
||||
@@ -142,6 +142,8 @@ namespace OpenRA
|
||||
public readonly ScreenMap ScreenMap;
|
||||
public readonly WorldType Type;
|
||||
|
||||
public readonly IValidateOrder[] OrderValidators;
|
||||
|
||||
readonly GameInformation gameInfo;
|
||||
|
||||
// Hide the OrderManager from mod code
|
||||
@@ -203,28 +205,14 @@ namespace OpenRA
|
||||
ActorMap = WorldActor.Trait<IActorMap>();
|
||||
ScreenMap = WorldActor.Trait<ScreenMap>();
|
||||
Selection = WorldActor.Trait<ISelection>();
|
||||
OrderValidators = WorldActor.TraitsImplementing<IValidateOrder>().ToArray();
|
||||
|
||||
// Reset mask
|
||||
LongBitSet<PlayerBitMask>.Reset();
|
||||
|
||||
// Add players
|
||||
// Create an isolated RNG to simplify synchronization between client and server player faction/spawn assignments
|
||||
var playerRandom = new MersenneTwister(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
|
||||
foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
|
||||
cmp.CreatePlayers(this);
|
||||
|
||||
// Set defaults for any unset stances
|
||||
foreach (var p in Players)
|
||||
{
|
||||
if (!p.Spectating)
|
||||
AllPlayersMask = AllPlayersMask.Union(p.PlayerMask);
|
||||
|
||||
foreach (var q in Players)
|
||||
{
|
||||
SetUpPlayerMask(p, q);
|
||||
|
||||
if (!p.Stances.ContainsKey(q))
|
||||
p.Stances[q] = Stance.Neutral;
|
||||
}
|
||||
}
|
||||
cmp.CreatePlayers(this, playerRandom);
|
||||
|
||||
Game.Sound.SoundVolumeModifier = 1.0f;
|
||||
|
||||
@@ -240,25 +228,6 @@ namespace OpenRA
|
||||
RulesContainTemporaryBlocker = map.Rules.Actors.Any(a => a.Value.HasTraitInfo<ITemporaryBlockerInfo>());
|
||||
}
|
||||
|
||||
void SetUpPlayerMask(Player p, Player q)
|
||||
{
|
||||
if (q.Spectating)
|
||||
return;
|
||||
|
||||
var bitSet = q.PlayerMask;
|
||||
|
||||
switch (p.Stances[q])
|
||||
{
|
||||
case Stance.Enemy:
|
||||
case Stance.Neutral:
|
||||
p.EnemyPlayersMask = p.EnemyPlayersMask.Union(bitSet);
|
||||
break;
|
||||
case Stance.Ally:
|
||||
p.AlliedPlayersMask = p.AlliedPlayersMask.Union(bitSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddToMaps(Actor self, IOccupySpace ios)
|
||||
{
|
||||
ActorMap.AddInfluence(self, ios);
|
||||
@@ -315,6 +284,8 @@ namespace OpenRA
|
||||
foreach (var player in Players)
|
||||
gameInfo.AddPlayer(player, OrderManager.LobbyInfo);
|
||||
|
||||
gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints;
|
||||
|
||||
var echo = OrderManager.Connection as EchoConnection;
|
||||
var rc = echo != null ? echo.Recorder : null;
|
||||
|
||||
@@ -461,7 +432,7 @@ namespace OpenRA
|
||||
foreach (var a in actors.Values)
|
||||
a.Tick();
|
||||
|
||||
ActorsWithTrait<ITick>().DoTimed(x => x.Trait.Tick(x.Actor), "Trait");
|
||||
ApplyToActorsWithTraitTimed<ITick>((Actor actor, ITick trait) => trait.Tick(actor), "Trait");
|
||||
|
||||
effects.DoTimed(e => e.Tick(this), "Effect");
|
||||
}
|
||||
@@ -473,7 +444,7 @@ namespace OpenRA
|
||||
// For things that want to update their render state once per tick, ignoring pause state
|
||||
public void TickRender(WorldRenderer wr)
|
||||
{
|
||||
ActorsWithTrait<ITickRender>().DoTimed(x => x.Trait.TickRender(wr, x.Actor), "Render");
|
||||
ApplyToActorsWithTraitTimed<ITickRender>((Actor actor, ITickRender trait) => trait.TickRender(wr, actor), "Render");
|
||||
ScreenMap.TickRender();
|
||||
}
|
||||
|
||||
@@ -532,6 +503,11 @@ namespace OpenRA
|
||||
return TraitDict.ActorsWithTrait<T>();
|
||||
}
|
||||
|
||||
public void ApplyToActorsWithTraitTimed<T>(Action<Actor, T> action, string text)
|
||||
{
|
||||
TraitDict.ApplyToActorsWithTraitTimed<T>(action, text);
|
||||
}
|
||||
|
||||
public IEnumerable<Actor> ActorsHavingTrait<T>()
|
||||
{
|
||||
return TraitDict.ActorsHavingTrait<T>();
|
||||
@@ -552,6 +528,15 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
|
||||
public void OnPlayerDisconnected(Player player)
|
||||
{
|
||||
var pi = gameInfo.GetPlayer(player);
|
||||
if (pi == null)
|
||||
return;
|
||||
|
||||
pi.DisconnectFrame = OrderManager.NetFrameNumber;
|
||||
}
|
||||
|
||||
public void RequestGameSave(string filename)
|
||||
{
|
||||
// Allow traits to save arbitrary data that will be passed back via IGameSaveTraitData.ResolveTraitData
|
||||
|
||||
@@ -87,11 +87,5 @@ namespace OpenRA
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AreMutualAllies(Player a, Player b)
|
||||
{
|
||||
return a.Stances[b] == Stance.Ally &&
|
||||
b.Stances[a] == Stance.Ally;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
6
OpenRA.Launcher/App.config
Normal file
6
OpenRA.Launcher/App.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<loadFromRemoteSources enabled="true" />
|
||||
</runtime>
|
||||
</configuration>
|
||||
72
OpenRA.Launcher/OpenRA.Launcher.csproj
Normal file
72
OpenRA.Launcher/OpenRA.Launcher.csproj
Normal file
@@ -0,0 +1,72 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework Condition="'$(Mono)' == ''">net5.0</TargetFramework>
|
||||
<TargetFramework Condition="'$(Mono)' != ''">netstandard2.1</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Optimize>true</Optimize>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
|
||||
<OutputPath>../bin</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ExternalConsole>false</ExternalConsole>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<CodeAnalysisRuleSet>..\OpenRA.ruleset</CodeAnalysisRuleSet>
|
||||
<Configurations>Release;Debug</Configurations>
|
||||
<AssemblyName>OpenRA</AssemblyName>
|
||||
<IsPublishable Condition="'$(CopyGenericLauncher)' == 'False'">false</IsPublishable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Windows'))">win-x64</TargetPlatform>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('Linux'))">linux-x64</TargetPlatform>
|
||||
<TargetPlatform Condition="$([MSBuild]::IsOsPlatform('OSX'))">osx-x64</TargetPlatform>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<!-- Work around an issue where Rider does not detect files in the project root using the default glob -->
|
||||
<Compile Include="**/*.cs" Exclude="$(DefaultItemExcludes)" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<Optimize>false</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(TargetPlatform)' == 'win-x86'">
|
||||
<Prefer32bit>true</Prefer32bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Red Alert'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Engine.EngineDir=".." Game.Mod=ra</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Dune 2000'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Engine.EngineDir=".." Game.Mod=d2k</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Tiberian Dawn'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Engine.EngineDir=".." Game.Mod=cnc</StartArguments>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(RunConfiguration)' == 'Tiberian Sun'">
|
||||
<StartAction>Project</StartAction>
|
||||
<StartArguments>Engine.EngineDir=".." Game.Mod=ts</StartArguments>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj" />
|
||||
<AdditionalFiles Include="../stylecop.json" />
|
||||
<AdditionalFiles Include="Properties/launchSettings.json" />
|
||||
</ItemGroup>
|
||||
<Target Name="DisableAnalyzers" BeforeTargets="CoreCompile" Condition="'$(Configuration)'=='Release'">
|
||||
<!-- Disable code style analysis on Release builds to improve compile-time performance -->
|
||||
<ItemGroup Condition="'$(Configuration)'=='Release'">
|
||||
<Analyzer Remove="@(Analyzer)" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<TrimmerRootAssembly Include="netstandard" />
|
||||
<TrimmerRootAssembly Include="System.IO.Pipes" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
39
OpenRA.Launcher/Program.cs
Normal file
39
OpenRA.Launcher/Program.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
#region Copyright & License Information
|
||||
/*
|
||||
* Copyright 2007-2020 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.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRA.Launcher
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static int Main(string[] args)
|
||||
{
|
||||
if (Debugger.IsAttached || args.Contains("--just-die"))
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
|
||||
AppDomain.CurrentDomain.UnhandledException += (_, e) => ExceptionHandler.HandleFatalError((Exception)e.ExceptionObject);
|
||||
|
||||
try
|
||||
{
|
||||
return (int)Game.InitializeAndRun(args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ExceptionHandler.HandleFatalError(e);
|
||||
return (int)RunStatus.Error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,19 +2,19 @@
|
||||
"profiles": {
|
||||
"Tiberian Dawn": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "Game.Mod=cnc"
|
||||
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=cnc"
|
||||
},
|
||||
"Red Alert": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "Game.Mod=ra"
|
||||
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=ra"
|
||||
},
|
||||
"Dune 2000": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "Game.Mod=d2k"
|
||||
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=d2k"
|
||||
},
|
||||
"Tiberian Sun": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "Game.Mod=ts"
|
||||
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=ts"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,14 +24,14 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
readonly INotifyInfiltration[] notifiers;
|
||||
Actor enterActor;
|
||||
|
||||
public Infiltrate(Actor self, Target target, Infiltrates infiltrates)
|
||||
: base(self, target, Color.Crimson)
|
||||
public Infiltrate(Actor self, in Target target, Infiltrates infiltrates, Color? targetLineColor)
|
||||
: base(self, target, targetLineColor)
|
||||
{
|
||||
this.infiltrates = infiltrates;
|
||||
notifiers = self.TraitsImplementing<INotifyInfiltration>().ToArray();
|
||||
}
|
||||
|
||||
protected override void TickInner(Actor self, Target target, bool targetIsDeadOrHiddenActor)
|
||||
protected override void TickInner(Actor self, in Target target, bool targetIsDeadOrHiddenActor)
|
||||
{
|
||||
if (infiltrates.IsTraitDisabled)
|
||||
Cancel(self, true);
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
readonly Minelayer minelayer;
|
||||
readonly AmmoPool[] ammoPools;
|
||||
readonly IMove movement;
|
||||
readonly IMoveInfo moveInfo;
|
||||
readonly RearmableInfo rearmableInfo;
|
||||
|
||||
List<CPos> minefield;
|
||||
@@ -37,6 +38,7 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
minelayer = self.Trait<Minelayer>();
|
||||
ammoPools = self.TraitsImplementing<AmmoPool>().ToArray();
|
||||
movement = self.Trait<IMove>();
|
||||
moveInfo = self.Info.TraitInfo<IMoveInfo>();
|
||||
rearmableInfo = self.Info.TraitInfoOrDefault<RearmableInfo>();
|
||||
this.minefield = minefield;
|
||||
}
|
||||
@@ -69,7 +71,7 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
if (rearmableInfo != null && ammoPools.Any(p => p.Info.Name == minelayer.Info.AmmoPoolName && !p.HasAmmo))
|
||||
{
|
||||
// Rearm (and possibly repair) at rearm building, then back out here to refill the minefield some more
|
||||
rearmTarget = self.World.Actors.Where(a => self.Owner.Stances[a.Owner] == Stance.Ally && rearmableInfo.RearmActors.Contains(a.Info.Name))
|
||||
rearmTarget = self.World.Actors.Where(a => self.Owner.RelationshipWith(a.Owner) == PlayerRelationship.Ally && rearmableInfo.RearmActors.Contains(a.Info.Name))
|
||||
.ClosestTo(self);
|
||||
|
||||
if (rearmTarget == null)
|
||||
@@ -118,17 +120,17 @@ namespace OpenRA.Mods.Cnc.Activities
|
||||
public override IEnumerable<TargetLineNode> TargetLineNodes(Actor self)
|
||||
{
|
||||
if (returnToBase)
|
||||
yield return new TargetLineNode(Target.FromActor(rearmTarget), Color.Green);
|
||||
yield return new TargetLineNode(Target.FromActor(rearmTarget), moveInfo.GetTargetLineColor());
|
||||
|
||||
if (minefield == null || minefield.Count == 0)
|
||||
yield break;
|
||||
|
||||
var nextCell = NextValidCell(self);
|
||||
if (nextCell != null)
|
||||
yield return new TargetLineNode(Target.FromCell(self.World, nextCell.Value), Color.Crimson);
|
||||
yield return new TargetLineNode(Target.FromCell(self.World, nextCell.Value), minelayer.Info.TargetLineColor);
|
||||
|
||||
foreach (var c in minefield)
|
||||
yield return new TargetLineNode(Target.FromCell(self.World, c), Color.Crimson, tile: minelayer.Tile);
|
||||
yield return new TargetLineNode(Target.FromCell(self.World, c), minelayer.Info.TargetLineColor, tile: minelayer.Tile);
|
||||
}
|
||||
|
||||
static bool CanLayMine(Actor self, CPos p)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user