Compare commits

..

1 Commits

Author SHA1 Message Date
Matthias Mailänder
157db79ae7 Enable itch app auto updates on Windows. 2020-09-06 13:50:50 +02:00
866 changed files with 21290 additions and 26375 deletions

View File

@@ -1,14 +1,11 @@
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."

View File

@@ -1,6 +1,6 @@
---
name: Crash report
about: Report a game crash. Check the FAQ first https://github.com/OpenRA/OpenRA/wiki/FAQ#common-issues
about: Report a game crash.
title: My game crashed
labels: Crash
assignees: ''

View File

@@ -1,55 +0,0 @@
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

View File

@@ -1,107 +0,0 @@
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

View File

@@ -1,88 +0,0 @@
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

View File

@@ -1,99 +0,0 @@
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 Normal file
View File

@@ -0,0 +1,92 @@
# 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

24
.vscode/launch.json vendored
View File

@@ -11,9 +11,9 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=cnc", "Engine.EngineDir=.."],
"preLaunchTask": "build",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=cnc"]
},
{
"name": "Launch (RA)",
@@ -25,9 +25,9 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=ra", "Engine.EngineDir=.."],
"preLaunchTask": "build",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ra"]
},
{
"name": "Launch (D2k)",
@@ -39,9 +39,9 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=d2k", "Engine.EngineDir=.."],
"preLaunchTask": "build",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=d2k"]
},
{
"name": "Launch (TS)",
@@ -53,9 +53,9 @@
"type": "mono"
},
"request": "launch",
"program": "${workspaceRoot}/bin/OpenRA.exe",
"args": ["Game.Mod=ts", "Engine.EngineDir=.."],
"preLaunchTask": "build",
"program": "${workspaceRoot}/OpenRA.Game.exe",
"cwd": "${workspaceRoot}",
"args": ["Game.Mod=ts"]
},
]
}

View File

@@ -145,7 +145,6 @@ Also thanks to:
* Tirili
* Tomas Einarsson (Mesacer)
* Tom van Leth (tovl)
* Trevor Nichols (ocdi)
* Tristan Keating (Kilkakon)
* Tristan Mühlbacher (MicroBit)
* UnknownProgrammer
@@ -189,8 +188,6 @@ 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.

View File

@@ -8,16 +8,18 @@ Windows
Compiling OpenRA requires the following dependencies:
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell) (included by default in recent Windows 10 versions)
* [.NET 5 SDK](https://dotnet.microsoft.com/download/dotnet/5.0) (or via Visual Studio)
* [.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)
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.
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.
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 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.
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.
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.
@@ -97,7 +99,7 @@ macOS
=====
Before compiling OpenRA you must install the following dependencies:
* [Mono >= 6.4](https://www.mono-project.com/download/stable/#download-mac)
* [Mono >= 5.18](https://www.mono-project.com/download/stable/#download-mac)
To compile OpenRA, run `make` from the command line. Run with `./launch-game.sh`.

335
Makefile
View File

@@ -12,22 +12,45 @@
# to check the engine and official mod dlls for code style violations, run:
# make check
#
# to compile and install Red Alert, Tiberian Dawn, and Dune 2000, run:
# to install, run:
# make [prefix=/foo] [bindir=/bar/bin] install
#
# to install Linux startup scripts, desktop files, icons, and MIME metadata
# make install-linux-shortcuts
# to install Linux startup scripts, desktop files and icons:
# make install-linux-shortcuts [DEBUG=false]
#
# to install Linux AppStream metadata
# make install-linux-appdata
# 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
#
# 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
# 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
######################### UTILITIES/SETTINGS ###########################
#
# Install locations for local installs and downstream packaging
# install locations
prefix ?= /usr/local
datarootdir ?= $(prefix)/share
datadir ?= $(datarootdir)
@@ -37,21 +60,27 @@ libdir ?= $(prefix)/lib
gameinstalldir ?= $(libdir)/openra
BIN_INSTALL_DIR = $(DESTDIR)$(bindir)
DATA_INSTALL_DIR = $(DESTDIR)$(datadir)
OPENRA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
DATA_INSTALL_DIR = $(DESTDIR)$(gameinstalldir)
# Toolchain
CWD = $(shell pwd)
MSBUILD = msbuild -verbosity:m -nologo
MONO = mono
# install tools
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
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
# Toolchain
MSBUILD = msbuild -verbosity:m -nologo
# Detect target platform for dependencies if not given by the user
# Enable 32 bit builds while generating the windows installer
WIN32 = false
# dependencies
ifndef TARGETPLATFORM
UNAME_S := $(shell uname -s)
UNAME_M := $(shell uname -m)
@@ -66,70 +95,242 @@ endif
endif
endif
OPENRA_UTILITY = ENGINE_DIR=".." $(MONO) --debug bin/OpenRA.Utility.dll
##################### DEVELOPMENT BUILDS AND TESTS #####################
#
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) -p:Mono=true -p:DefineConstants="MONO"
ifeq ($(TARGETPLATFORM), unix-generic)
@./configure-system-libraries.sh
endif
@./fetch-geoip.sh
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 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
# program targets
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
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
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..."
@$(OPENRA_UTILITY) ts --check-yaml
@mono --debug OpenRA.Utility.exe ts --check-yaml
@echo
@echo "Testing Dune 2000 mod MiniYAML..."
@$(OPENRA_UTILITY) d2k --check-yaml
@mono --debug OpenRA.Utility.exe d2k --check-yaml
@echo
@echo "Testing Tiberian Dawn mod MiniYAML..."
@$(OPENRA_UTILITY) cnc --check-yaml
@mono --debug OpenRA.Utility.exe cnc --check-yaml
@echo
@echo "Testing Red Alert mod MiniYAML..."
@$(OPENRA_UTILITY) ra --check-yaml
@mono --debug OpenRA.Utility.exe ra --check-yaml
############# LOCAL INSTALLATION AND DOWNSTREAM PACKAGING ##############
########################## MAKE/INSTALL RULES ##########################
#
all: core
core:
@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)
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
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
@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'
@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
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: core install-engine install-common-mod-files install-default-mods
@$(CP) *.sh "$(DATA_INSTALL_DIR)"
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-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-appdata:
@sh -c '. ./packaging/functions.sh; install_linux_appdata $(CWD) $(DATA_INSTALL_DIR) cnc d2k ra'
@$(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"
help:
@echo 'to compile, run:'
@@ -144,14 +345,22 @@ help:
@echo 'to check the engine and official mod dlls for code style violations, run:'
@echo ' make test'
@echo
@echo 'to compile and install Red Alert, Tiberian Dawn, and Dune 2000 run:'
@echo ' make [prefix=/foo] install'
@echo 'to install, run:'
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
@echo
@echo 'to install Linux startup scripts, desktop files, icons, and MIME metadata'
@echo ' make install-linux-shortcuts'
@echo 'to install Linux startup scripts, desktop files and icons'
@echo ' make install-linux-shortcuts [DEBUG=false]'
@echo
@echo 'to install Linux AppStream metadata'
@echo ' make install-linux-appdata'
@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'
########################### MAKEFILE SETTINGS ##########################
#
@@ -159,4 +368,4 @@ help:
.SUFFIXES:
.PHONY: all clean check check-scripts test version install install-linux-shortcuts install-linux-appdata help
.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

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Activities
public readonly Color Color;
public readonly Sprite Tile;
public TargetLineNode(in Target target, Color color, Sprite tile = null)
public TargetLineNode(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.
@@ -74,7 +74,7 @@ namespace OpenRA.Activities
// drop valid activities queued after it. Walk the queue until we find a valid activity or
// (more likely) run out of activities.
while (first != null && first.State == ActivityState.Done)
first = first.nextActivity;
first = first.NextActivity;
return first;
}
@@ -120,8 +120,7 @@ namespace OpenRA.Activities
lastRun = Tick(self);
// Avoid a single tick delay if the childactivity was just queued.
var ca = ChildActivity;
if (ca != null && ca.State == ActivityState.Queued)
if (ChildActivity != null && ChildActivity.State == ActivityState.Queued)
{
if (ChildHasPriority)
lastRun = TickChild(self) && finishing;
@@ -207,18 +206,18 @@ namespace OpenRA.Activities
public void Queue(Activity activity)
{
var it = this;
while (it.nextActivity != null)
it = it.nextActivity;
it.nextActivity = activity;
if (NextActivity != null)
NextActivity.Queue(activity);
else
NextActivity = activity;
}
public void QueueChild(Activity activity)
{
if (childActivity != null)
childActivity.Queue(activity);
if (ChildActivity != null)
ChildActivity.Queue(activity);
else
childActivity = activity;
ChildActivity = activity;
}
/// <summary>
@@ -270,21 +269,15 @@ namespace OpenRA.Activities
public IEnumerable<T> ActivitiesImplementing<T>(bool includeChildren = true) where T : IActivityInterface
{
// Skips Done child and next activities
if (includeChildren)
{
var ca = ChildActivity;
if (ca != null)
foreach (var a in ca.ActivitiesImplementing<T>())
yield return a;
}
if (includeChildren && ChildActivity != null)
foreach (var a in ChildActivity.ActivitiesImplementing<T>())
yield return a;
if (this is T)
yield return (T)(object)this;
var na = NextActivity;
if (na != null)
foreach (var a in na.ActivitiesImplementing<T>())
if (NextActivity != null)
foreach (var a in NextActivity.ActivitiesImplementing<T>())
yield return a;
}
}

View File

@@ -102,7 +102,6 @@ namespace OpenRA
readonly IFacing facing;
readonly IHealth health;
readonly IResolveOrder[] resolveOrders;
readonly IRenderModifier[] renderModifiers;
readonly IRender[] renders;
readonly IMouseBounds[] mouseBounds;
@@ -110,10 +109,9 @@ namespace OpenRA
readonly IDefaultVisibility defaultVisibility;
readonly INotifyBecomingIdle[] becomingIdles;
readonly INotifyIdle[] tickIdles;
readonly IEnumerable<ITargetablePositions> enabledTargetablePositions;
readonly ITargetablePositions[] targetablePositions;
WPos[] staticTargetablePositions;
bool created;
bool setStaticTargetablePositions;
internal Actor(World world, string name, TypeDictionary initDict)
{
@@ -141,61 +139,42 @@ namespace OpenRA
throw new NotImplementedException("No rules definition for unit " + name);
Info = world.Map.Rules.Actors[name];
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())
foreach (var trait in Info.TraitsInConstructOrder())
{
var trait = traitInfo.Create(init);
AddTrait(trait);
AddTrait(trait.Create(init));
// 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)); }
// 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>();
}
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)
@@ -229,12 +208,7 @@ namespace OpenRA
foreach (var notify in allObserverNotifiers)
notify(this, readOnlyConditionCache);
// 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: Some 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
@@ -430,12 +404,6 @@ namespace OpenRA
});
}
public void ResolveOrder(Order order)
{
foreach (var r in resolveOrders)
r.ResolveOrder(this, order);
}
// TODO: move elsewhere.
public void ChangeOwner(Player newOwner)
{
@@ -539,8 +507,9 @@ namespace OpenRA
if (staticTargetablePositions != null)
return staticTargetablePositions;
if (enabledTargetablePositions.Any())
return enabledTargetablePositions.SelectMany(tp => tp.TargetablePositions(this));
var enabledTargetablePositionTraits = targetablePositions.Where(Exts.IsTraitEnabled);
if (enabledTargetablePositionTraits.Any())
return enabledTargetablePositionTraits.SelectMany(tp => tp.TargetablePositions(this));
return new[] { CenterPosition };
}

View File

@@ -121,7 +121,7 @@ namespace OpenRA
mods[key] = mod;
}
internal void Register(Manifest mod, string launchPath, IEnumerable<string> launchArgs, ModRegistration registration)
internal void Register(Manifest mod, string launchPath, 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", new[] { "Game.Mod=" + mod.Id }.Concat(launchArgs).JoinWith(", "))
new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id)
}));
using (var stream = mod.Package.GetStream("icon.png"))

View File

@@ -120,14 +120,7 @@ namespace OpenRA
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k)
where V : new()
{
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;
return d.GetOrAdd(k, _ => new V());
}
public static V GetOrAdd<K, V>(this Dictionary<K, V> d, K k, Func<K, V> createFn)

View File

@@ -66,11 +66,17 @@ namespace OpenRA
static readonly ConcurrentCache<Type, FieldLoadInfo[]> TypeLoadInfo =
new ConcurrentCache<Type, FieldLoadInfo[]>(BuildTypeLoadInfo);
static readonly ConcurrentCache<MemberInfo, bool> MemberHasTranslateAttribute =
new ConcurrentCache<MemberInfo, bool>(member => member.HasAttribute<TranslateAttribute>());
static readonly ConcurrentCache<string, BooleanExpression> BooleanExpressionCache =
new ConcurrentCache<string, BooleanExpression>(expression => new BooleanExpression(expression));
static readonly ConcurrentCache<string, IntegerExpression> IntegerExpressionCache =
new ConcurrentCache<string, IntegerExpression>(expression => new IntegerExpression(expression));
static readonly object TranslationsLock = new object();
static Dictionary<string, string> translations;
public static void Load(object self, MiniYaml my)
{
var loadInfo = TypeLoadInfo[self.GetType()];
@@ -207,7 +213,11 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(string))
{
if (field != null && MemberHasTranslateAttribute[field] && value != null)
return Regex.Replace(value, "@[^@]+@", m => Translate(m.Value.Substring(1, m.Value.Length - 2)), RegexOptions.Compiled);
return value;
}
else if (fieldType == typeof(Color))
{
if (value != null && Color.TryParse(value, out var color))
@@ -425,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<>) || fieldType.GetGenericTypeDefinition() == typeof(List<>)))
else if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(HashSet<>))
{
var set = Activator.CreateInstance(fieldType);
if (value == null)
@@ -684,8 +694,34 @@ namespace OpenRA
return null;
}
}
public static string Translate(string key)
{
if (string.IsNullOrEmpty(key))
return key;
lock (TranslationsLock)
{
if (translations == null)
return key;
if (!translations.TryGetValue(key, out var value))
return key;
return value;
}
}
public static void SetTranslations(IDictionary<string, string> translations)
{
lock (TranslationsLock)
FieldLoader.translations = new Dictionary<string, string>(translations);
}
}
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class TranslateAttribute : Attribute { }
[AttributeUsage(AttributeTargets.Field)]
public sealed class FieldFromYamlKeyAttribute : FieldLoader.SerializeAttribute
{

View File

@@ -94,7 +94,7 @@ namespace OpenRA
return ((Array)v).Cast<object>().Select(FormatValue).JoinWith(", ");
}
if (t.IsGenericType && (t.GetGenericTypeDefinition() == typeof(HashSet<>) || t.GetGenericTypeDefinition() == typeof(List<>)))
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(HashSet<>))
{
return ((System.Collections.IEnumerable)v).Cast<object>().Select(FormatValue).JoinWith(", ");
}

View File

@@ -17,7 +17,6 @@ using System.Net;
using System.Text;
using ICSharpCode.SharpZipLib.Checksum;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.FileFormats
@@ -26,15 +25,12 @@ namespace OpenRA.FileFormats
{
static readonly byte[] Signature = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
public int Width { get; private set; }
public int Height { get; private set; }
public Color[] Palette { get; private set; }
public byte[] Data { get; private set; }
public SpriteFrameType Type { get; private set; }
public int Width { get; set; }
public int Height { get; set; }
public Color[] Palette { get; set; }
public byte[] Data { get; set; }
public Dictionary<string, string> EmbeddedData = new Dictionary<string, string>();
public int PixelStride { get { return Type == SpriteFrameType.Indexed8 ? 1 : Type == SpriteFrameType.Rgb24 ? 3 : 4; } }
public Png(Stream s)
{
if (!Verify(s))
@@ -42,8 +38,9 @@ namespace OpenRA.FileFormats
s.Position += 8;
var headerParsed = false;
var isPaletted = false;
var is24Bit = false;
var data = new List<byte>();
Type = SpriteFrameType.Rgba32;
while (true)
{
@@ -68,12 +65,14 @@ namespace OpenRA.FileFormats
var bitDepth = ms.ReadUInt8();
var colorType = (PngColorType)ms.ReadByte();
if (IsPaletted(bitDepth, colorType))
Type = SpriteFrameType.Indexed8;
else if (colorType == PngColorType.Color)
Type = SpriteFrameType.Rgb24;
isPaletted = IsPaletted(bitDepth, colorType);
is24Bit = colorType == PngColorType.Color;
Data = new byte[Width * Height * PixelStride];
var dataLength = Width * Height;
if (!isPaletted)
dataLength *= 4;
Data = new byte[dataLength];
var compression = ms.ReadByte();
/*var filter = */ms.ReadByte();
@@ -134,28 +133,39 @@ namespace OpenRA.FileFormats
{
using (var ds = new InflaterInputStream(ns))
{
var pxStride = PixelStride;
var rowStride = Width * pxStride;
var pxStride = isPaletted ? 1 : is24Bit ? 3 : 4;
var srcStride = Width * pxStride;
var destStride = Width * (isPaletted ? 1 : 4);
var prevLine = new byte[rowStride];
var prevLine = new byte[srcStride];
for (var y = 0; y < Height; y++)
{
var filter = (PngFilter)ds.ReadByte();
var line = ds.ReadBytes(rowStride);
var line = ds.ReadBytes(srcStride);
for (var i = 0; i < rowStride; i++)
for (var i = 0; i < srcStride; i++)
line[i] = i < pxStride
? UnapplyFilter(filter, line[i], 0, prevLine[i], 0)
: UnapplyFilter(filter, line[i], line[i - pxStride], prevLine[i], prevLine[i - pxStride]);
Array.Copy(line, 0, Data, y * rowStride, rowStride);
if (is24Bit)
{
// Fold alpha channel into RGB data
for (var i = 0; i < line.Length / 3; i++)
{
Array.Copy(line, 3 * i, Data, y * destStride + 4 * i, 3);
Data[y * destStride + 4 * i + 3] = 255;
}
}
else
Array.Copy(line, 0, Data, y * destStride, line.Length);
prevLine = line;
}
}
}
if (Type == SpriteFrameType.Indexed8 && Palette == null)
if (isPaletted && Palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
return;
@@ -165,7 +175,7 @@ namespace OpenRA.FileFormats
}
}
public Png(byte[] data, SpriteFrameType type, int width, int height, Color[] palette = null,
public Png(byte[] data, int width, int height, Color[] palette = null,
Dictionary<string, string> embeddedData = null)
{
var expectLength = width * height;
@@ -175,46 +185,11 @@ namespace OpenRA.FileFormats
if (data.Length != expectLength)
throw new InvalidDataException("Input data does not match expected length");
Type = type;
Width = width;
Height = height;
switch (type)
{
case SpriteFrameType.Indexed8:
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
// Data is already in a compatible format
Data = data;
if (type == SpriteFrameType.Indexed8)
Palette = palette;
break;
}
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
{
// Convert to big endian
Data = new byte[data.Length];
var stride = PixelStride;
for (var i = 0; i < width * height; i++)
{
Data[stride * i] = data[stride * i + 2];
Data[stride * i + 1] = data[stride * i + 1];
Data[stride * i + 2] = data[stride * i + 0];
if (type == SpriteFrameType.Bgra32)
Data[stride * i + 3] = data[stride * i + 3];
}
break;
}
default:
throw new InvalidDataException("Unhandled SpriteFrameType {0}".F(type));
}
Palette = palette;
Data = data;
if (embeddedData != null)
EmbeddedData = embeddedData;
@@ -299,8 +274,9 @@ namespace OpenRA.FileFormats
header.Write(IPAddress.HostToNetworkOrder(Height));
header.WriteByte(8); // Bit depth
var colorType = Type == SpriteFrameType.Indexed8 ? PngColorType.Indexed | PngColorType.Color :
Type == SpriteFrameType.Rgb24 ? PngColorType.Color : PngColorType.Color | PngColorType.Alpha;
var colorType = Palette != null
? PngColorType.Indexed | PngColorType.Color
: PngColorType.Color | PngColorType.Alpha;
header.WriteByte((byte)colorType);
header.WriteByte(0); // Compression
@@ -310,7 +286,7 @@ namespace OpenRA.FileFormats
WritePngChunk(output, "IHDR", header);
}
var alphaPalette = false;
bool alphaPalette = false;
if (Palette != null)
{
using (var palette = new MemoryStream())
@@ -342,12 +318,12 @@ namespace OpenRA.FileFormats
{
using (var compressed = new DeflaterOutputStream(data))
{
var rowStride = Width * PixelStride;
var stride = Width * (Palette != null ? 1 : 4);
for (var y = 0; y < Height; y++)
{
// Write uncompressed scanlines for simplicity
compressed.WriteByte(0);
compressed.Write(Data, y * rowStride, rowStride);
compressed.Write(Data, y * stride, stride);
}
compressed.Flush();

View File

@@ -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 (!resolvedPath.Contains("|") && Directory.Exists(resolvedPath))
if (!filename.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 && !path.StartsWith("^"))
if (explicitSplit > 0)
{
var parent = path.Substring(0, explicitSplit);
var filename = path.Substring(explicitSplit + 1);

View File

@@ -12,7 +12,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.FileSystem
{
@@ -33,11 +32,10 @@ namespace OpenRA.FileSystem
{
get
{
// 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);
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);
}
}

View File

@@ -17,7 +17,6 @@ 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;
@@ -73,10 +72,10 @@ namespace OpenRA
return om;
}
public static string TimestampedFilename(bool includemilliseconds = false, string extra = "")
static string TimestampedFilename(bool includemilliseconds = false)
{
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
return ModData.Manifest.Id + extra + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
return ModData.Manifest.Id + "-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
}
static void JoinInner(OrderManager om)
@@ -171,13 +170,11 @@ 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)
@@ -192,14 +189,6 @@ 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();
}
@@ -262,24 +251,18 @@ namespace OpenRA
public static void InitializeSettings(Arguments args)
{
Settings = new Settings(Path.Combine(Platform.SupportDir, "settings.yaml"), args);
Settings = new Settings(Platform.ResolvePath(Path.Combine(Platform.SupportDirPrefix, "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);
@@ -289,7 +272,7 @@ namespace OpenRA
// Load the engine version as early as possible so it can be written to exception logs
try
{
EngineVersion = File.ReadAllText(Path.Combine(Platform.EngineDir, "VERSION")).Trim();
EngineVersion = File.ReadAllText(Platform.ResolvePath(Path.Combine(".", "VERSION"))).Trim();
}
catch { }
@@ -297,7 +280,6 @@ 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
@@ -329,17 +311,10 @@ namespace OpenRA
Settings.Game.Platform = p;
try
{
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 rendererPath = Platform.ResolvePath(Path.Combine(".", "OpenRA.Platforms." + p + ".dll"));
var assembly = Assembly.LoadFile(rendererPath);
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
#endif
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
if (platformType == null)
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
@@ -366,7 +341,7 @@ namespace OpenRA
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ?
FieldLoader.GetValue<string[]>("Engine.ModsPath", modSearchArg) :
new[] { Path.Combine(Platform.EngineDir, "mods") };
new[] { Path.Combine(".", "mods") };
Mods = new InstalledMods(modSearchPaths, explicitModPaths);
Console.WriteLine("Internal mods:");
@@ -379,24 +354,14 @@ namespace OpenRA
if (modID != null && Mods.TryGetValue(modID, out _))
{
var launchPath = args.GetValue("Engine.LaunchPath", null);
var launchArgs = new List<string>();
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
// Sanitize input from platform-specific launchers
// Process.Start requires paths to not be quoted, even if they contain spaces
if (launchPath != null && launchPath.First() == '"' && launchPath.Last() == '"')
if (launchPath.First() == '"' && launchPath.Last() == '"')
launchPath = launchPath.Substring(1, launchPath.Length - 2);
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);
ExternalMods.Register(Mods[modID], launchPath, ModRegistration.User);
if (ExternalMods.TryGetValue(ExternalMod.MakeKey(Mods[modID]), out var activeMod))
ExternalMods.ClearInvalidRegistrations(activeMod, ModRegistration.User);
@@ -445,7 +410,7 @@ namespace OpenRA
ModData = new ModData(Mods[mod], Mods, true);
LocalPlayerProfile = new LocalPlayerProfile(Path.Combine(Platform.SupportDir, Settings.Game.AuthProfile), ModData.Manifest.Get<PlayerDatabase>());
LocalPlayerProfile = new LocalPlayerProfile(Platform.ResolvePath(Path.Combine("^", Settings.Game.AuthProfile)), ModData.Manifest.Get<PlayerDatabase>());
if (!ModData.LoadScreen.BeforeLoad())
return;
@@ -484,8 +449,6 @@ namespace OpenRA
ChromeMetrics.TryGet("ChatMessageColor", out chatMessageColor);
ChromeMetrics.TryGet("SystemMessageColor", out systemMessageColor);
if (!ChromeMetrics.TryGet("SystemMessageLabel", out systemMessageLabel))
systemMessageLabel = "Battlefield Control";
ModData.LoadScreen.StartGame(args);
}
@@ -555,8 +518,6 @@ namespace OpenRA
static volatile ActionQueue delayedActions = new ActionQueue();
static Color systemMessageColor = Color.White;
static Color chatMessageColor = Color.White;
static string systemMessageLabel;
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
@@ -565,7 +526,7 @@ namespace OpenRA
using (new PerfTimer("Renderer.SaveScreenshot"))
{
var mod = ModData.Manifest.Metadata;
var directory = Path.Combine(Platform.SupportDir, "Screenshots", ModData.Manifest.Id, mod.Version);
var directory = Platform.ResolvePath(Platform.SupportDirPrefix, "Screenshots", ModData.Manifest.Id, mod.Version);
Directory.CreateDirectory(directory);
var filename = TimestampedFilename(true);
@@ -888,7 +849,7 @@ namespace OpenRA
public static void AddSystemLine(string text)
{
AddSystemLine(systemMessageLabel, text);
AddSystemLine("Battlefield Control", text);
}
public static void AddSystemLine(string name, string text)
@@ -946,11 +907,9 @@ 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);

View File

@@ -35,7 +35,6 @@ 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; } }
@@ -119,13 +118,11 @@ 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.DisplaySpawnPoint == 0,
IsRandomSpawnPoint = runtimePlayer.SpawnPoint != client.SpawnPoint,
Fingerprint = client.Fingerprint
};
@@ -159,10 +156,6 @@ 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;
@@ -186,9 +179,6 @@ 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
}
}

View File

@@ -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, tileSet, mapSequences);
new SequenceProvider(fileSystem, modData, ts, mapSequences);
var modelSequences = dr.ModelSequences;
if (mapModelSequences != null)

View File

@@ -163,7 +163,7 @@ namespace OpenRA.GameRules
}
/// <summary>Checks if the weapon is valid against (can target) the target.</summary>
public bool IsValidAgainst(in Target target, World world, Actor firedBy)
public bool IsValidAgainst(Target target, World world, Actor firedBy)
{
if (target.Type == TargetType.Actor)
return IsValidAgainst(target.Actor, firedBy);
@@ -220,24 +220,20 @@ namespace OpenRA.GameRules
}
/// <summary>Applies all the weapon's warheads to the target.</summary>
public void Impact(in Target target, WarheadArgs args)
public void Impact(Target target, WarheadArgs args)
{
var world = args.SourceActor.World;
foreach (var warhead in Warheads)
{
if (warhead.Delay > 0)
{
// 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)));
}
world.AddFrameEndTask(w => w.Add(new DelayedImpact(warhead.Delay, warhead, target, 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(in Target target, Actor firedBy)
public void Impact(Target target, Actor firedBy)
{
// The impact will happen immediately at target.CenterPosition.
var args = new WarheadArgs

View File

@@ -15,6 +15,7 @@ namespace OpenRA
{
public class GameSpeed
{
[Translate]
public readonly string Name = "Default";
public readonly int Timestep = 40;
public readonly int OrderLatency = 3;

View File

@@ -69,16 +69,9 @@ namespace OpenRA.Graphics
// Hotspot is specified relative to the center of the frame
var hotspot = f.Offset.ToInt2() - kv.Value.Hotspot - new int2(f.Size) / 2;
// Resolve indexed data to real colours
var data = f.Data;
var type = f.Type;
if (type == SpriteFrameType.Indexed8)
{
data = ConvertIndexedToBgra(kv.Key, f, palette);
type = SpriteFrameType.Bgra32;
}
c.Sprites[c.Length++] = sheetBuilder.Add(data, type, f.Size, 0, hotspot);
// SheetBuilder expects data in BGRA
var data = FrameToBGRA(kv.Key, f, palette);
c.Sprites[c.Length++] = sheetBuilder.Add(data, f.Size, 0, hotspot);
// Bounds relative to the hotspot
c.Bounds = Rectangle.Union(c.Bounds, new Rectangle(hotspot, f.Size));
@@ -224,27 +217,34 @@ namespace OpenRA.Graphics
Update();
}
public static byte[] ConvertIndexedToBgra(string name, ISpriteFrame frame, ImmutablePalette palette)
public static byte[] FrameToBGRA(string name, ISpriteFrame frame, ImmutablePalette palette)
{
if (frame.Type != SpriteFrameType.Indexed8)
throw new ArgumentException("ConvertIndexedToBgra requires input frames to be indexed.", nameof(frame));
// Data is already in BGRA format
if (frame.Type == SpriteFrameType.BGRA)
return frame.Data;
// Cursors may be either native BGRA or Indexed.
// Indexed sprites are converted to BGRA using the referenced palette.
// All palettes must be explicitly referenced, even if they are embedded in the sprite.
if (palette == null)
if (frame.Type == SpriteFrameType.Indexed && palette == null)
throw new InvalidOperationException("Cursor sequence `{0}` attempted to load an indexed sprite but does not define Palette".F(name));
var width = frame.Size.Width;
var height = frame.Size.Height;
var data = new byte[4 * width * height];
unsafe
for (var j = 0; j < height; j++)
{
// Cast the data to an int array so we can copy the src data directly
fixed (byte* bd = &data[0])
for (var i = 0; i < width; i++)
{
var rgba = (uint*)bd;
for (var j = 0; j < height; j++)
for (var i = 0; i < width; i++)
rgba[j * width + i] = palette[frame.Data[j * width + i]];
var bytes = BitConverter.GetBytes(palette[frame.Data[j * width + i]]);
var c = 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];
}
}

View File

@@ -31,10 +31,8 @@ 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;
Length = Frames.Length - Start;
else if (d.ContainsKey("Length"))
Length = Exts.ParseIntegerInvariant(d["Length"].Value);
else if (d.ContainsKey("End"))
@@ -42,7 +40,10 @@ namespace OpenRA.Graphics
else
Length = 1;
Frames = Frames.Take(Length).ToArray();
Frames = cache[cursorSrc]
.Skip(Start)
.Take(Length)
.ToArray();
if (d.ContainsKey("X"))
{

View File

@@ -45,7 +45,6 @@ namespace OpenRA.Graphics
public interface IModelCache : IDisposable
{
IModel GetModel(string model);
IModel GetModelSequence(string model, string sequence);
bool HasModelSequence(string model, string sequence);
IVertexBuffer<Vertex> VertexBuffer { get; }
@@ -67,11 +66,6 @@ namespace OpenRA.Graphics
public void Dispose() { }
public IModel GetModel(string model)
{
throw new NotImplementedException();
}
public IModel GetModelSequence(string model, string sequence)
{
throw new NotImplementedException();

View File

@@ -79,8 +79,8 @@ namespace OpenRA.Graphics
}
public ModelRenderProxy RenderAsync(
WorldRenderer wr, IEnumerable<ModelAnimation> models, in WRot camera, float scale,
float[] groundNormal, in WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
WorldRenderer wr, IEnumerable<ModelAnimation> models, WRot camera, float scale,
float[] groundNormal, WRot lightSource, float[] lightAmbientColor, float[] lightDiffuseColor,
PaletteReference color, PaletteReference normals, PaletteReference shadowPalette)
{
if (!isInFrame)

View File

@@ -17,8 +17,6 @@ namespace OpenRA
{
public enum GLProfile
{
Automatic,
ANGLE,
Modern,
Embedded,
Legacy
@@ -26,7 +24,7 @@ namespace OpenRA
public interface IPlatform
{
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile, bool enableLegacyGL);
IPlatformWindow CreateWindow(Size size, WindowMode windowMode, float scaleModifier, int batchSize, int videoDisplay, GLProfile profile);
ISoundEngine CreateSound(string device);
IFont CreateFont(byte[] data);
}
@@ -103,7 +101,8 @@ namespace OpenRA
{
void Bind();
void SetData(T[] vertices, int length);
void SetData(T[] vertices, int offset, int start, int length);
void SetData(T[] vertices, int start, int length);
void SetData(IntPtr data, int start, int length);
}
public interface IShader

View File

@@ -30,7 +30,7 @@ namespace OpenRA.Graphics
public interface ITintableRenderable
{
IRenderable WithTint(in float3 newTint);
IRenderable WithTint(float3 newTint);
}
public interface IFinalizedRenderable

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Graphics
this.parent = parent;
}
public void DrawLine(in float3 start, in float3 end, float width, Color startColor, Color endColor)
public void DrawLine(float3 start, 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(in float3 start, in float3 end, float width, Color color)
public void DrawLine(float3 start, 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(in float3 a, in float3 da, in float3 b, in float3 db)
float3 IntersectionOf(float3 a, float3 da, float3 b, 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(in float3 tl, in float3 br, float width, Color color)
public void DrawRect(float3 tl, 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(in float3 a, in float3 b, in float3 c, Color color)
public void FillTriangle(float3 a, float3 b, 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(in float3 tl, in float3 br, Color color)
public void FillRect(float3 tl, 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(in float3 a, in float3 b, in float3 c, in float3 d, Color color)
public void FillRect(float3 a, float3 b, float3 c, 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(in float3 a, in float3 b, in float3 c, in float3 d, Color topLeftColor, Color topRightColor, Color bottomRightColor, Color bottomLeftColor)
public void FillRect(float3 a, float3 b, float3 c, 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(in float3 xyz, Color color)
static Vertex VertexWithColor(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(in float3 tl, in float3 br, Color color, int vertices = 32)
public void FillEllipse(float3 tl, float3 br, Color color, int vertices = 32)
{
// TODO: Create an ellipse polygon instead
var a = (br.X - tl.X) / 2;

View File

@@ -22,7 +22,7 @@ namespace OpenRA.Graphics
this.parent = parent;
}
public void DrawSprite(Sprite s, in float3 location, in float3 size)
public void DrawSprite(Sprite s, float3 location, 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, in float3 location)
public void DrawSprite(Sprite s, 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, in float3 a, in float3 b, in float3 c, in float3 d)
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, 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, in float3 location, in float3 size, in float3 tint)
public void DrawSpriteWithTint(Sprite s, float3 location, float3 size, 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, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
{
if (s.Channel != TextureChannel.RGBA)
throw new InvalidOperationException("DrawRGBASprite requires a RGBA sprite.");

View File

@@ -41,20 +41,21 @@ namespace OpenRA.Graphics
public interface ISpriteSequenceLoader
{
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, string tileSet, SpriteCache cache, MiniYamlNode node);
Action<string> OnMissingSpriteError { get; set; }
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, TileSet tileSet, SpriteCache cache, MiniYamlNode node);
}
public class SequenceProvider : IDisposable
{
readonly ModData modData;
readonly string tileSet;
readonly TileSet 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, string tileSet, MiniYaml additionalSequences)
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, MiniYaml additionalSequences)
{
this.modData = modData;
this.tileSet = tileSet;
@@ -78,8 +79,6 @@ namespace OpenRA.Graphics
return seq;
}
public IEnumerable<string> Images { get { return sequences.Value.Keys; } }
public bool HasSequence(string unitName)
{
return sequences.Value.ContainsKey(unitName);
@@ -105,11 +104,10 @@ namespace OpenRA.Graphics
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
var items = new Dictionary<string, UnitSequences>();
foreach (var node in nodes)
foreach (var n in nodes)
{
// Nodes starting with ^ are inheritable but never loaded directly
if (node.Key.StartsWith(ActorInfo.AbstractActorPrefix, StringComparison.Ordinal))
continue;
// Work around the loop closure issue in older versions of C#
var node = n;
var key = node.Value.ToLines(node.Key).JoinWith("|");

View File

@@ -79,17 +79,21 @@ namespace OpenRA.Graphics
public Png AsPng()
{
if (Type == SheetType.Indexed)
throw new InvalidOperationException("AsPng() cannot be called on Indexed sheets.");
var data = GetData();
return new Png(GetData(), SpriteFrameType.Bgra32, Size.Width, Size.Height);
// Convert BGRA to RGBA
for (var i = 0; i < Size.Width * Size.Height; i++)
{
var temp = data[i * 4];
data[i * 4] = data[i * 4 + 2];
data[i * 4 + 2] = temp;
}
return new Png(data, Size.Width, Size.Height);
}
public Png AsPng(TextureChannel channel, IPalette pal)
{
if (Type != SheetType.Indexed)
throw new InvalidOperationException("AsPng(TextureChannel, IPalette) can only be called on Indexed sheets.");
var d = GetData();
var plane = new byte[Size.Width * Size.Height];
var dataStride = 4 * Size.Width;
@@ -103,7 +107,7 @@ namespace OpenRA.Graphics
for (var i = 0; i < Palette.Size; i++)
palColors[i] = pal.GetColor(i);
return new Png(plane, SpriteFrameType.Bgra32, Size.Width, Size.Height, palColors);
return new Png(plane, Size.Width, Size.Height, palColors);
}
public void CreateBuffer()

View File

@@ -52,15 +52,8 @@ namespace OpenRA.Graphics
{
switch (t)
{
case SpriteFrameType.Indexed8:
return SheetType.Indexed;
// Util.FastCopyIntoChannel will automatically convert these to BGRA
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
return SheetType.BGRA;
case SpriteFrameType.Indexed: return SheetType.Indexed;
case SpriteFrameType.BGRA: return SheetType.BGRA;
default: throw new NotImplementedException("Unknown SpriteFrameType {0}".F(t));
}
}
@@ -81,16 +74,16 @@ namespace OpenRA.Graphics
this.margin = margin;
}
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Type, frame.Size, 0, frame.Offset); }
public Sprite Add(byte[] src, SpriteFrameType type, Size size) { return Add(src, type, size, 0, float3.Zero); }
public Sprite Add(byte[] src, SpriteFrameType type, Size size, float zRamp, in float3 spriteOffset)
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)
{
// Don't bother allocating empty sprites
if (size.Width == 0 || size.Height == 0)
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
var rect = Allocate(size, zRamp, spriteOffset);
Util.FastCopyIntoChannel(rect, src, type);
Util.FastCopyIntoChannel(rect, src);
current.CommitBufferedData();
return rect;
}
@@ -103,6 +96,15 @@ namespace OpenRA.Graphics
return rect;
}
public Sprite Add(Size size, byte paletteIndex)
{
var data = new byte[size.Width * size.Height];
for (var i = 0; i < data.Length; i++)
data[i] = paletteIndex;
return Add(data, size);
}
TextureChannel? NextChannel(TextureChannel t)
{
var nextChannel = (int)t + (int)Type;
@@ -113,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, in float3 spriteOffset, float scale = 1f)
public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset, float scale = 1f)
{
if (imageSize.Width + p.X + margin > current.Size.Width)
{

View File

@@ -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, in float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha, float scale = 1f)
{
Sheet = sheet;
Bounds = bounds;

View File

@@ -239,15 +239,7 @@ namespace OpenRA.Graphics
return new int2(0, size);
var lines = text.Split('\n');
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;
return new int2((int)Math.Ceiling(lines.Max(lineWidth)), lines.Length * size);
}
GlyphInfo CreateGlyph(char c)

View File

@@ -18,29 +18,7 @@ using OpenRA.Primitives;
namespace OpenRA.Graphics
{
/// <summary>
/// Describes the format of the pixel data in a ISpriteFrame.
/// Note that the channel order is defined for little-endian bytes, so BGRA corresponds
/// to a 32bit ARGB value, such as that returned by Color.ToArgb()!
/// </summary>
public enum SpriteFrameType
{
// 8 bit index into an external palette
Indexed8,
// 32 bit color such as returned by Color.ToArgb() or the bmp file format
// (remember that little-endian systems place the little bits in the first byte!)
Bgra32,
// Like BGRA, but without an alpha channel
Bgr24,
// 32 bit color in big-endian format, like png
Rgba32,
// Like RGBA, but without an alpha channel
Rgb24
}
public enum SpriteFrameType { Indexed, BGRA }
public interface ISpriteLoader
{
@@ -69,7 +47,7 @@ namespace OpenRA.Graphics
public class SpriteCache
{
public readonly Cache<SheetType, SheetBuilder> SheetBuilders;
public readonly Cache<SpriteFrameType, SheetBuilder> SheetBuilders;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
@@ -79,7 +57,7 @@ namespace OpenRA.Graphics
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
{
SheetBuilders = new Cache<SheetType, SheetBuilder>(t => new SheetBuilder(t));
SheetBuilders = new Cache<SpriteFrameType, SheetBuilder>(t => new SheetBuilder(SheetBuilder.FrameTypeToSheetType(t)));
this.fileSystem = fileSystem;
this.loaders = loaders;
@@ -125,7 +103,7 @@ namespace OpenRA.Graphics
{
if (unloaded[i] != null)
{
sprite[i] = SheetBuilders[SheetBuilder.FrameTypeToSheetType(unloaded[i].Type)].Add(unloaded[i]);
sprite[i] = SheetBuilders[unloaded[i].Type].Add(unloaded[i]);
unloaded[i] = null;
}
}

View File

@@ -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(in float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
public IRenderable WithTint(float3 newTint) { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, newTint, isDecoration, ignoreWorldTint); }
float3 ScreenPosition(WorldRenderer wr)
{

View File

@@ -10,21 +10,17 @@
#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[SheetCount];
readonly Sheet[] sheets = new Sheet[7];
BlendMode currentBlend = BlendMode.Alpha;
int nv = 0;
@@ -43,7 +39,7 @@ namespace OpenRA.Graphics
{
for (var i = 0; i < ns; i++)
{
shader.SetTexture(SheetIndexToTextureName[i], sheets[i].GetTexture());
shader.SetTexture("Texture{0}".F(i), sheets[i].GetTexture());
sheets[i] = null;
}
@@ -108,43 +104,43 @@ namespace OpenRA.Graphics
return new int2(sheetIndex, secondarySheetIndex);
}
internal void DrawSprite(Sprite s, in float3 location, float paletteTextureIndex, in float3 size)
internal void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, 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, in float3 location, PaletteReference pal)
public void DrawSprite(Sprite s, float3 location, PaletteReference pal)
{
DrawSprite(s, location, pal.TextureIndex, s.Size);
}
public void DrawSprite(Sprite s, in float3 location, PaletteReference pal, float3 size)
public void DrawSprite(Sprite s, float3 location, PaletteReference pal, float3 size)
{
DrawSprite(s, location, pal.TextureIndex, size);
}
public void DrawSprite(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d)
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, 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, in float3 location, float paletteTextureIndex, in float3 size, in float3 tint)
internal void DrawSpriteWithTint(Sprite s, float3 location, float paletteTextureIndex, float3 size, 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, in float3 location, PaletteReference pal, in float3 size, in float3 tint)
public void DrawSpriteWithTint(Sprite s, float3 location, PaletteReference pal, float3 size, float3 tint)
{
DrawSpriteWithTint(s, location, pal.TextureIndex, size, tint);
}
public void DrawSpriteWithTint(Sprite s, in float3 a, in float3 b, in float3 c, in float3 d, in float3 tint)
public void DrawSpriteWithTint(Sprite s, float3 a, float3 b, float3 c, float3 d, float3 tint)
{
var samplers = SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, samplers, 0, tint, nv);

View File

@@ -137,7 +137,7 @@ namespace OpenRA.Graphics
dirtyRows.Add(uv.V);
}
public void Update(MPos uv, Sprite sprite, in float3 pos, bool ignoreTint)
public void Update(MPos uv, Sprite sprite, float3 pos, bool ignoreTint)
{
if (sprite != null)
{
@@ -183,7 +183,15 @@ namespace OpenRA.Graphics
continue;
var rowOffset = rowStride * row;
vertexBuffer.SetData(vertices, rowOffset, rowOffset, rowStride);
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);
}
}
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Graphics
readonly MersenneTwister random;
TileSet tileset;
public Theater(TileSet tileset, Action<uint, string> onMissingImage = null)
public Theater(TileSet tileset)
{
this.tileset = tileset;
var allocated = false;
@@ -63,31 +63,9 @@ namespace OpenRA.Graphics
foreach (var i in t.Value.Images)
{
ISpriteFrame[] allFrames;
if (onMissingImage != null)
{
try
{
allFrames = frameCache[i];
}
catch (FileNotFoundException)
{
onMissingImage(t.Key, i);
continue;
}
}
else
allFrames = frameCache[i];
var allFrames = frameCache[i];
var frameCount = tileset.EnableDepth ? allFrames.Length / 2 : allFrames.Length;
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));
var indices = t.Value.Frames != null ? t.Value.Frames : Enumerable.Range(0, frameCount);
variants.Add(indices.Select(j =>
{
var f = allFrames[j];
@@ -104,16 +82,15 @@ namespace OpenRA.Graphics
if (sheetBuilder == null)
sheetBuilder = new SheetBuilder(SheetBuilder.FrameTypeToSheetType(f.Type), allocate);
else if (type != sheetBuilder.Type)
throw new YamlException("Sprite type mismatch. Terrain sprites must all be either Indexed or RGBA.");
throw new InvalidDataException("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, f.Type);
Util.FastCopyIntoChannel(s, f.Data);
if (tileset.EnableDepth)
{
var ss = sheetBuilder.Allocate(f.Size, zRamp, offset);
var depthFrame = allFrames[j + frameCount];
Util.FastCopyIntoChannel(ss, depthFrame.Data, depthFrame.Type);
Util.FastCopyIntoChannel(ss, allFrames[j + frameCount].Data);
// s and ss are guaranteed to use the same sheet
// because of the custom terrain sheet allocation
@@ -130,26 +107,15 @@ 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));
}
// 1x1px transparent tile
if (sheetBuilder.Type == SheetType.BGRA)
missingTile = sheetBuilder.Add(new byte[4], SpriteFrameType.Bgra32, new Size(1, 1));
else
missingTile = sheetBuilder.Add(new byte[1], SpriteFrameType.Indexed8, new Size(1, 1));
missingTile = sheetBuilder.Add(new byte[sheetBuilder.Type == SheetType.BGRA ? 4 : 1], new Size(1, 1));
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))
@@ -172,7 +138,10 @@ namespace OpenRA.Graphics
for (var x = 0; x < template.Size.X; x++)
{
var tile = new TerrainTile(template.Id, (byte)(i++));
if (!tileset.TryGetTileInfo(tile, out var tileInfo))
var tileInfo = tileset.GetTileInfo(tile);
// Empty tile
if (tileInfo == null)
continue;
var sprite = TileSprite(tile);

View File

@@ -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, in float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, in float3 size, in float3 tint)
public static void FastCreateQuad(Vertex[] vertices, float3 o, Sprite r, int2 samplers, float paletteTextureIndex, int nv, float3 size, 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,
in float3 a, in float3 b, in float3 c, in float3 d,
float3 a, float3 b, float3 c, float3 d,
Sprite r, int2 samplers, float paletteTextureIndex,
in float3 tint, int nv)
float3 tint, int nv)
{
float sl = 0;
float st = 0;
@@ -62,7 +62,7 @@ namespace OpenRA.Graphics
vertices[nv + 5] = new Vertex(a, r.Left, r.Top, sl, st, paletteTextureIndex, fAttribC, tint);
}
public static void FastCopyIntoChannel(Sprite dest, byte[] src, SpriteFrameType srcType)
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
{
var destData = dest.Sheet.GetData();
var width = dest.Bounds.Width;
@@ -85,34 +85,12 @@ namespace OpenRA.Graphics
{
for (var i = 0; i < width; i++)
{
byte r, g, b, a;
switch (srcType)
{
case SpriteFrameType.Bgra32:
case SpriteFrameType.Bgr24:
{
b = src[k++];
g = src[k++];
r = src[k++];
a = srcType == SpriteFrameType.Bgra32 ? src[k++] : (byte)255;
break;
}
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
r = src[k++];
g = src[k++];
b = src[k++];
a = srcType == SpriteFrameType.Rgba32 ? src[k++] : (byte)255;
break;
}
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(srcType));
}
var r = src[k++];
var g = src[k++];
var b = src[k++];
var a = src[k++];
var cc = Color.FromArgb(a, r, g, b);
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
}
}
@@ -161,29 +139,16 @@ namespace OpenRA.Graphics
for (var i = 0; i < width; i++)
{
Color cc;
switch (src.Type)
if (src.Palette == null)
{
case SpriteFrameType.Indexed8:
{
cc = src.Palette[src.Data[k++]];
break;
}
case SpriteFrameType.Rgba32:
case SpriteFrameType.Rgb24:
{
var r = src.Data[k++];
var g = src.Data[k++];
var b = src.Data[k++];
var a = src.Type == SpriteFrameType.Rgba32 ? src.Data[k++] : (byte)255;
cc = Color.FromArgb(a, r, g, b);
break;
}
// Pngs don't support BGR[A], so no need to include them here
default:
throw new InvalidOperationException("Unknown SpriteFrameType {0}".F(src.Type));
var r = src.Data[k++];
var g = src.Data[k++];
var b = src.Data[k++];
var a = src.Data[k++];
cc = Color.FromArgb(a, r, g, b);
}
else
cc = src.Palette[src.Data[k++]];
data[(y + j) * destStride + x + i] = PremultiplyAlpha(cc).ToArgb();
}

View File

@@ -28,13 +28,13 @@ namespace OpenRA.Graphics
// Color tint
public readonly float R, G, B;
public Vertex(in float3 xyz, float s, float t, float u, float v, float p, float c)
public Vertex(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(in float3 xyz, float s, float t, float u, float v, float p, float c, in float3 tint)
public Vertex(float3 xyz, float s, float t, float u, float v, float p, float c, 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, in float3 tint)
public Vertex(float x, float y, float z, float s, float t, float u, float v, float p, float c, 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)

View File

@@ -282,7 +282,7 @@ namespace OpenRA.Graphics
// Try and find the closest cell
if (candidates.Count > 0)
{
return candidates.MinBy(uv =>
return candidates.OrderBy(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;
}).ToCPos(map);
}).First().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(in float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
public int2 WorldToViewPx(float3 world) { return ((Zoom / graphicSettings.UIScale) * (world - TopLeft).XY).ToInt2(); }
public void Center(IEnumerable<Actor> actors)
{

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Graphics
{
public sealed class WorldRenderer : IDisposable
{
public static readonly Func<IRenderable, int> RenderableZPositionComparisonKey =
public static readonly Func<IRenderable, int> RenderableScreenZPositionComparisonKey =
r => ZPosition(r.Pos, r.ZOffset);
public readonly Size TileSize;
@@ -133,8 +133,7 @@ namespace OpenRA.Graphics
foreach (var e in World.ScreenMap.RenderableEffectsInBox(Viewport.TopLeft, Viewport.BottomRight))
renderablesBuffer.AddRange(e.Render(this));
// Renderables must be ordered using a stable sorting algorithm to avoid flickering artefacts
foreach (var renderable in renderablesBuffer.OrderBy(RenderableZPositionComparisonKey))
foreach (var renderable in renderablesBuffer.OrderBy(RenderableScreenZPositionComparisonKey))
preparedRenderables.Add(renderable.PrepareRender(this));
// PERF: Reuse collection to avoid allocations.

View File

@@ -60,7 +60,7 @@ namespace OpenRA
public readonly string[]
Rules, ServerTraits,
Sequences, ModelSequences, Cursors, Chrome, Assemblies, ChromeLayout,
Weapons, Voices, Notifications, Music, TileSets,
Weapons, Voices, Notifications, Music, Translations, TileSets,
ChromeMetrics, MapCompatibility, Missions, Hotkeys;
public readonly IReadOnlyDictionary<string, string> Packages;
@@ -73,7 +73,7 @@ namespace OpenRA
readonly string[] reservedModuleNames =
{
"Include", "Metadata", "Folders", "MapFolders", "Packages", "Rules",
"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,25 +89,7 @@ namespace OpenRA
{
Id = modId;
Package = package;
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();
yaml = new MiniYaml(null, MiniYaml.FromStream(package.GetStream("mod.yaml"), "mod.yaml")).ToDictionary();
Metadata = FieldLoader.Load<ModMetadata>(yaml["Metadata"]);
@@ -128,6 +110,7 @@ namespace OpenRA
Voices = YamlList(yaml, "Voices");
Notifications = YamlList(yaml, "Notifications");
Music = YamlList(yaml, "Music");
Translations = YamlList(yaml, "Translations");
TileSets = YamlList(yaml, "TileSets");
ChromeMetrics = YamlList(yaml, "ChromeMetrics");
Missions = YamlList(yaml, "Missions");

View File

@@ -17,7 +17,6 @@ using System.Reflection;
using System.Text;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Traits;
@@ -174,6 +173,7 @@ namespace OpenRA
new MapField("Voices", "VoiceDefinitions", required: false),
new MapField("Music", "MusicDefinitions", required: false),
new MapField("Notifications", "NotificationDefinitions", required: false),
new MapField("Translations", "TranslationDefinitions", required: false)
};
// Format versions
@@ -204,8 +204,7 @@ namespace OpenRA
public readonly MiniYaml VoiceDefinitions;
public readonly MiniYaml MusicDefinitions;
public readonly MiniYaml NotificationDefinitions;
public readonly Dictionary<CPos, TerrainTile> ReplacedInvalidTerrainTiles = new Dictionary<CPos, TerrainTile>();
public readonly MiniYaml TranslationDefinitions;
// Generated data
public readonly MapGrid Grid;
@@ -288,6 +287,7 @@ 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 +310,7 @@ namespace OpenRA
Tiles.CellEntryChanged += UpdateRamp;
}
Tiles.Clear(tileset.DefaultTerrainTile);
Tiles.Clear(tileRef);
PostInit();
}
@@ -331,9 +331,6 @@ 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>();
@@ -430,18 +427,12 @@ namespace OpenRA
foreach (var uv in AllCells.MapCoords)
CustomTerrain[uv] = byte.MaxValue;
// Replace invalid tiles and cache ramp state
// Cache initial ramp state
var tileset = Rules.TileSet;
foreach (var uv in AllCells.MapCoords)
foreach (var uv in AllCells)
{
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;
var tile = tileset.GetTileInfo(Tiles[uv]);
Ramp[uv] = tile != null ? tile.RampType : (byte)0;
}
AllEdgeCells = UpdateEdgeCells();
@@ -449,7 +440,8 @@ namespace OpenRA
void UpdateRamp(CPos cell)
{
Ramp[cell] = Rules.TileSet.GetTileInfo(Tiles[cell]).RampType;
var tile = Rules.TileSet.GetTileInfo(Tiles[cell]);
Ramp[cell] = tile != null ? tile.RampType : (byte)0;
}
void InitializeCellProjection()
@@ -675,26 +667,32 @@ namespace OpenRA
Color left, right;
var tileset = Rules.TileSet;
var type = tileset.GetTileInfo(Tiles[uv]);
if (type.MinColor != type.MaxColor)
if (type != null)
{
left = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), type.MinColor, type.MaxColor);
right = Exts.ColorLerp(Game.CosmeticRandom.NextFloat(), 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));
}
}
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 = right = Color.Black;
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)>();
@@ -714,73 +712,76 @@ namespace OpenRA
foreach (var worldimpsi in worldimpsis)
worldimpsi.PopulateMapPreviewSignatureCells(this, worldActorInfo, null, positions);
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++)
using (var stream = new MemoryStream())
{
for (var x = 0; x < width; x++)
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 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)
for (var x = 0; x < width; x++)
{
// Odd rows are shifted right by 1px
var dx = uv.V & 1;
var xOffset = pxStride * (2 * x + dx);
if (x + dx > 0)
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 z = y * stride + xOffset - pxStride;
// 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 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, SpriteFrameType.Bgra32, bitmapWidth, height);
return png.Save();
var png = new Png(minimapData, bitmapWidth, height);
return png.Save();
}
}
public bool Contains(CPos cell)
@@ -1281,12 +1282,16 @@ 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 (allowOutsideBounds ? Tiles.Contains(t) : Contains(t))
if (valid(t))
yield return t;
}
}

View File

@@ -38,8 +38,6 @@ 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;
@@ -72,10 +70,13 @@ namespace OpenRA
try
{
// HACK: If the path is inside the the support directory then we may need to create it
// 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);
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);
}
package = modData.ModFiles.OpenPackage(name);
}
@@ -141,9 +142,12 @@ namespace OpenRA
name = name.Substring(1);
// Don't try to open the map directory in the support directory if it doesn't exist
var resolved = Platform.ResolvePath(name);
if (resolved.StartsWith(Platform.SupportDir) && (!Directory.Exists(resolved) || !File.Exists(resolved)))
continue;
if (Platform.IsPathRelativeToSupportDirectory(name))
{
var resolved = Platform.ResolvePath(name);
if (!Directory.Exists(resolved) || !File.Exists(resolved))
continue;
}
using (var package = (IReadWritePackage)modData.ModFiles.OpenPackage(name))
{

View File

@@ -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", stringPool: cache.StringPool)).ToDictionary();
yaml = new MiniYaml(null, MiniYaml.FromStream(yamlStream, "map.yaml")).ToDictionary();
}
Package = p;

View File

@@ -32,19 +32,7 @@ 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;

View File

@@ -22,24 +22,11 @@ namespace OpenRA
: base(gridType, size) { }
// Resolve an array index from map coordinates.
public int Index(PPos uv)
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]
{

View File

@@ -23,8 +23,6 @@ namespace OpenRA
}
public override int GetHashCode() { return Type.GetHashCode() ^ Index.GetHashCode(); }
public override string ToString() { return Type + "," + Index; }
}
public struct ResourceTile

View File

@@ -54,6 +54,13 @@ 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);
@@ -65,11 +72,8 @@ namespace OpenRA
tileInfo = new TerrainTileInfo[Size.X * Size.Y];
foreach (var node in nodes)
{
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));
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));
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
}
@@ -81,11 +85,8 @@ namespace OpenRA
var i = 0;
foreach (var node in nodes)
{
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));
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));
tileInfo[key] = LoadTileInfo(tileSet, node.Value);
}
@@ -161,14 +162,14 @@ namespace OpenRA
.ToArray();
if (TerrainInfo.Length >= byte.MaxValue)
throw new YamlException("Too many terrain types.");
throw new InvalidDataException("Too many terrain types.");
for (byte i = 0; i < TerrainInfo.Length; i++)
{
var tt = TerrainInfo[i].Type;
if (terrainIndexByType.ContainsKey(tt))
throw new YamlException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
throw new InvalidDataException("Duplicate terrain type '{0}' in '{1}'.".F(tt, filepath));
terrainIndexByType.Add(tt, i);
}
@@ -221,30 +222,25 @@ namespace OpenRA
public byte GetTerrainIndex(TerrainTile r)
{
var tile = Templates[r.Type][r.Index];
if (tile.TerrainType != byte.MaxValue)
return tile.TerrainType;
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;
}
return defaultWalkableTerrainIndex;
}
public TerrainTileInfo GetTileInfo(TerrainTile r)
{
return Templates[r.Type][r.Index];
if (!Templates.TryGetValue(r.Type, out var tpl))
return null;
return tpl.Contains(r.Index) ? tpl[r.Index] : null;
}
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); } }
}
}

View File

@@ -99,10 +99,7 @@ namespace OpenRA
public MiniYaml Clone()
{
var clonedNodes = new MiniYamlNodes(Nodes.Count);
foreach (var node in Nodes)
clonedNodes.Add(node.Clone());
return new MiniYaml(Value, clonedNodes);
return new MiniYaml(Value, Nodes.Select(n => n.Clone()).ToList());
}
public Dictionary<string, MiniYaml> ToDictionary()
@@ -151,11 +148,8 @@ namespace OpenRA
return nd.ContainsKey(s) ? nd[s].Nodes : new List<MiniYamlNode>();
}
static List<MiniYamlNode> FromLines(IEnumerable<string> lines, string filename, bool discardCommentsAndWhitespace, Dictionary<string, string> stringPool)
static List<MiniYamlNode> FromLines(IEnumerable<string> lines, string filename, bool discardCommentsAndWhitespace)
{
if (stringPool == null)
stringPool = new Dictionary<string, string>();
var levels = new List<List<MiniYamlNode>>();
levels.Add(new List<MiniYamlNode>());
@@ -269,10 +263,6 @@ 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));
@@ -280,33 +270,23 @@ namespace OpenRA
}
}
foreach (var nodes in levels)
nodes.TrimExcess();
return levels[0];
}
public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
public static List<MiniYamlNode> FromFile(string path, bool discardCommentsAndWhitespace = true)
{
return FromLines(File.ReadAllLines(path), path, discardCommentsAndWhitespace, stringPool);
return FromLines(File.ReadAllLines(path), path, discardCommentsAndWhitespace);
}
public static List<MiniYamlNode> FromStream(Stream s, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
public static List<MiniYamlNode> FromStream(Stream s, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true)
{
IEnumerable<string> Lines(StreamReader reader)
{
string line;
while ((line = reader.ReadLine()) != null)
yield return line;
}
using (var reader = new StreamReader(s))
return FromLines(Lines(reader), fileName, discardCommentsAndWhitespace, stringPool);
return FromString(reader.ReadToEnd(), fileName, discardCommentsAndWhitespace);
}
public static List<MiniYamlNode> FromString(string text, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true, Dictionary<string, string> stringPool = null)
public static List<MiniYamlNode> FromString(string text, string fileName = "<no filename available>", bool discardCommentsAndWhitespace = true)
{
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None), fileName, discardCommentsAndWhitespace, stringPool);
return FromLines(text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.None), fileName, discardCommentsAndWhitespace);
}
public static List<MiniYamlNode> Merge(IEnumerable<List<MiniYamlNode>> sources)
@@ -350,7 +330,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>(node.Nodes.Count);
var resolved = new List<MiniYamlNode>();
// Inheritance is tracked from parent->child, but not from child->parentsiblings.
inherited = new Dictionary<string, MiniYamlNode.SourceLocation>(inherited);
@@ -381,7 +361,6 @@ namespace OpenRA
MergeIntoResolved(n, resolved, tree, inherited);
}
resolved.TrimExcess();
return resolved;
}
@@ -418,7 +397,6 @@ namespace OpenRA
}
}
ret.TrimExcess();
return ret;
}
@@ -459,7 +437,6 @@ namespace OpenRA
ret.Add(merged);
}
ret.TrimExcess();
return ret;
}

View File

@@ -79,6 +79,7 @@ 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");
@@ -107,7 +108,7 @@ namespace OpenRA
defaultSequences = Exts.Lazy(() =>
{
var items = DefaultTileSets.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Key, null));
var items = DefaultTileSets.ToDictionary(t => t.Key, t => new SequenceProvider(DefaultFileSystem, this, t.Value, null));
return (IReadOnlyDictionary<string, SequenceProvider>)(new ReadOnlyDictionary<string, SequenceProvider>(items));
});
@@ -138,6 +139,42 @@ namespace OpenRA
public IEnumerable<string> Languages { get; private set; }
void LoadTranslations(Map map)
{
var selectedTranslations = new Dictionary<string, string>();
var defaultTranslations = new Dictionary<string, string>();
if (!Manifest.Translations.Any())
{
Languages = new string[0];
return;
}
var yaml = MiniYaml.Load(map, Manifest.Translations, map.TranslationDefinitions);
Languages = yaml.Select(t => t.Key).ToArray();
foreach (var y in yaml)
{
if (y.Key == Game.Settings.Graphics.Language)
selectedTranslations = y.Value.ToDictionary(my => my.Value ?? "");
else if (y.Key == Game.Settings.Graphics.DefaultLanguage)
defaultTranslations = y.Value.ToDictionary(my => my.Value ?? "");
}
var translations = new Dictionary<string, string>();
foreach (var tkv in defaultTranslations.Concat(selectedTranslations))
{
if (translations.ContainsKey(tkv.Key))
continue;
if (selectedTranslations.ContainsKey(tkv.Key))
translations.Add(tkv.Key, selectedTranslations[tkv.Key]);
else
translations.Add(tkv.Key, tkv.Value);
}
FieldLoader.SetTranslations(translations);
}
public Map PrepareMap(string uid)
{
LoadScreen?.Display();
@@ -149,6 +186,8 @@ namespace OpenRA
using (new Support.PerfTimer("Map"))
map = new Map(this, MapCache[uid].Package);
LoadTranslations(map);
// Reinitialize all our assets
InitializeLoaders(map);

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Network
if (lastClientFrame == -1)
lastClientFrame = framePackets
.Where(x => x.Value.ContainsKey(clientId))
.Select(x => x.Key).MaxByOrDefault(x => x);
.Select(x => x.Key).OrderBy(x => x).LastOrDefault();
clientQuitTimes[clientId] = lastClientFrame;
}

View File

@@ -117,7 +117,7 @@ namespace OpenRA.Network
LastOrdersFrame = rs.ReadInt32();
LastSyncFrame = rs.ReadInt32();
lastSyncPacket = rs.ReadBytes(Order.SyncHashOrderLength);
lastSyncPacket = rs.ReadBytes(5);
var globalSettings = MiniYaml.FromString(rs.ReadString(Encoding.UTF8, Connection.MaxOrderLength));
GlobalSettings = Session.Global.Deserialize(globalSettings[0].Value);
@@ -190,12 +190,6 @@ 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;
}
@@ -288,7 +282,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, Order.SyncHashOrderLength);
file.Write(lastSyncPacket, 0, 5);
var globalSettingsNodes = new List<MiniYamlNode>() { GlobalSettings.Serialize() };
file.WriteString(Encoding.UTF8, globalSettingsNodes.WriteToString());

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Network
"Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
// Current server state
"Map", "State", "MaxPlayers", "Protected", "Authentication", "DisabledSpawnPoints"
"Map", "State", "MaxPlayers", "Protected", "Authentication"
};
public const int ProtocolVersion = 2;
@@ -132,9 +132,6 @@ 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)
@@ -229,7 +226,6 @@ 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)

View File

@@ -49,13 +49,10 @@ 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 ref readonly Target Target => ref target;
public readonly Target Target;
public readonly Actor[] GroupedActors;
public string TargetString;
@@ -66,18 +63,15 @@ namespace OpenRA
public OrderType Type = OrderType.Fields;
public bool SuppressVisualFeedback;
public ref readonly Target VisualFeedbackTarget => ref visualFeedbackTarget;
public Target VisualFeedbackTarget;
public Player Player { get { return Subject != null ? Subject.Owner : 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)
Order(string orderString, Actor subject, Target target, string targetString, bool queued, Actor[] extraActors, CPos extraLocation, uint extraData, Actor[] groupedActors = null)
{
OrderString = orderString ?? "";
Subject = subject;
this.target = target;
Target = target;
TargetString = targetString;
Queued = queued;
ExtraActors = extraActors;
@@ -281,15 +275,9 @@ 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, in Target target, bool queued, Actor[] extraActors = null, Actor[] groupedActors = null)
public Order(string orderString, Actor subject, 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;

View File

@@ -31,14 +31,13 @@ namespace OpenRA.Network
return ret;
}
public static byte[] SerializeSync(int sync, ulong defeatState)
public static byte[] SerializeSync(int sync)
{
var ms = new MemoryStream(Order.SyncHashOrderLength);
var ms = new MemoryStream(1 + 4);
using (var writer = new BinaryWriter(ms))
{
writer.Write((byte)OrderType.SyncHash);
writer.Write(sync);
writer.Write(defeatState);
}
return ms.GetBuffer();

View File

@@ -47,10 +47,10 @@ namespace OpenRA.Network
internal int GameSaveLastFrame = -1;
internal int GameSaveLastSyncFrame = -1;
readonly List<Order> localOrders = new List<Order>();
readonly List<Order> localImmediateOrders = new List<Order>();
List<Order> localOrders = new List<Order>();
List<Order> localImmediateOrders = new List<Order>();
readonly List<ChatLine> chatCache = new List<ChatLine>();
List<ChatLine> chatCache = new List<ChatLine>();
public readonly ReadOnlyList<ChatLine> ChatCache;
@@ -123,16 +123,8 @@ 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 > 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;
}
else if (packet.Length >= 5 && packet[4] == (byte)OrderType.SyncHash)
CheckSync(packet);
}
else if (frame == 0)
immediatePackets.Add((clientId, packet));
else
@@ -200,16 +192,9 @@ namespace OpenRA.Network
UnitOrders.ProcessOrder(this, World, order.Client, order.Order);
if (NetFrameNumber + FramesAhead >= GameSaveLastSyncFrame)
{
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));
}
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(World.SyncHash()));
else
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0, 0));
Connection.SendSync(NetFrameNumber, OrderIO.SerializeSync(0));
if (generateSyncReport)
using (new PerfSample("sync_report"))

View File

@@ -55,25 +55,28 @@ 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 > 4 && (packet[4] == (byte)OrderType.Disconnect || packet[4] == (byte)OrderType.SyncHash))
continue;
if (frame == 0)
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)
{
// Parse replay metadata from orders stream
var orders = packet.ToOrderList(null);

View File

@@ -10,7 +10,6 @@
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
@@ -26,7 +25,9 @@ namespace OpenRA.Network
static bool IsGameStart(byte[] data)
{
if (data.Length > 4 && (data[4] == (byte)OrderType.Disconnect || data[4] == (byte)OrderType.SyncHash))
if (data.Length == 5 && data[4] == (byte)OrderType.Disconnect)
return false;
if (data.Length >= 5 && data[4] == (byte)OrderType.SyncHash)
return false;
var frame = BitConverter.ToInt32(data, 0);
@@ -44,7 +45,7 @@ namespace OpenRA.Network
{
var filename = chooseFilename();
var mod = Game.ModData.Manifest;
var dir = Path.Combine(Platform.SupportDir, "Replays", mod.Id, mod.Metadata.Version);
var dir = Platform.ResolvePath(Platform.SupportDirPrefix, "Replays", mod.Id, mod.Metadata.Version);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
@@ -84,14 +85,6 @@ 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()

View File

@@ -26,8 +26,6 @@ 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)
@@ -71,9 +69,6 @@ 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;
}
}
@@ -272,10 +267,7 @@ namespace OpenRA.Network
public string Serialize()
{
var sessionData = new List<MiniYamlNode>()
{
new MiniYamlNode("DisabledSpawnPoints", FieldSaver.FormatValue(DisabledSpawnPoints))
};
var sessionData = new List<MiniYamlNode>();
foreach (var client in Clients)
sessionData.Add(client.Serialize());

View File

@@ -27,6 +27,13 @@ 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
@@ -39,13 +46,7 @@ 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;
}
@@ -301,7 +302,10 @@ 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);
@@ -332,27 +336,22 @@ namespace OpenRA.Network
default:
{
if (world == null)
break;
if (order.GroupedActors == null)
ResolveOrder(order, world, orderManager, clientId);
ResolveOrder(order);
else
foreach (var subject in order.GroupedActors)
ResolveOrder(Order.FromGroupedOrder(order, subject), world, orderManager, clientId);
ResolveOrder(Order.FromGroupedOrder(order, subject));
break;
}
}
}
static void ResolveOrder(Order order, World world, OrderManager orderManager, int clientId)
static void ResolveOrder(Order order)
{
if (order.Subject == null || order.Subject.IsDead)
return;
if (world.OrderValidators.All(vo => vo.OrderValidation(orderManager, world, clientId, order)))
order.Subject.ResolveOrder(order);
if (order.Subject != null && !order.Subject.IsDead)
foreach (var t in order.Subject.TraitsImplementing<IResolveOrder>())
t.ResolveOrder(order.Subject, order);
}
static void SetOrderLag(OrderManager o)

View File

@@ -15,7 +15,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
@@ -43,48 +42,25 @@ namespace OpenRA
if (resolvedPath == null)
throw new FileNotFoundException("Assembly `{0}` not found.".F(path));
LoadAssembly(assemblyList, 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);
}
assemblyList.Add(assembly);
}
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))
{
#if MONO
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);
}
}
#else
var loader = new AssemblyLoader(resolvedPath);
assembly = loader.LoadDefaultAssembly();
ResolvedAssemblies.Add(hash, assembly);
#endif
}
assemblyList.Add(assembly);
}
Assembly ResolveAssembly(object sender, ResolveEventArgs e)
{
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>netstandard2.1</TargetFramework>
<OutputType>Exe</OutputType>
<TargetFramework>net472</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Optimize>true</Optimize>
<UseVSHostingProcess>false</UseVSHostingProcess>
@@ -10,7 +10,7 @@
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AutoGenerateBindingRedirects>false</AutoGenerateBindingRedirects>
<RootNamespace>OpenRA</RootNamespace>
<OutputPath>../bin</OutputPath>
<OutputPath>..</OutputPath>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<PlatformTarget>AnyCPU</PlatformTarget>
<ExternalConsole>false</ExternalConsole>
@@ -31,18 +31,34 @@
<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.3.1" />
<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" />
</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" />
<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 -->

View File

@@ -196,17 +196,15 @@ namespace OpenRA.Orders
public readonly IOrderTargeter Order;
public readonly IIssueOrder Trait;
public readonly string Cursor;
public ref readonly Target Target => ref target;
public readonly Target Target;
readonly Target target;
public UnitOrderResult(Actor actor, IOrderTargeter order, IIssueOrder trait, string cursor, in Target target)
public UnitOrderResult(Actor actor, IOrderTargeter order, IIssueOrder trait, string cursor, Target target)
{
Actor = actor;
Order = order;
Trait = trait;
Cursor = cursor;
this.target = target;
Target = target;
}
}

View File

@@ -22,14 +22,12 @@ 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;
@@ -139,7 +137,7 @@ namespace OpenRA
}
// Use a local directory in the game root if it exists (shared with the system support dir)
var localSupportDir = Path.Combine(EngineDir, "Support");
var localSupportDir = Path.Combine(GameDir, "Support");
if (Directory.Exists(localSupportDir))
userSupportPath = systemSupportPath = localSupportDir + Path.DirectorySeparatorChar;
@@ -172,45 +170,7 @@ namespace OpenRA
userSupportPath = path;
}
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
public static string GameDir
{
get
{
@@ -229,25 +189,50 @@ namespace OpenRA
{
path = path.TrimEnd(' ', '\t');
if (path == "^SupportDir")
return SupportDir;
// Paths starting with ^ are relative to the support dir
if (IsPathRelativeToSupportDirectory(path))
path = SupportDir + path.Substring(1);
if (path == "^EngineDir")
return EngineDir;
// Paths starting with . are relative to the game dir
if (path == ".")
return GameDir;
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);
if (path.StartsWith("./", StringComparison.Ordinal) || path.StartsWith(".\\", StringComparison.Ordinal))
path = GameDir + path.Substring(2);
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);
}
}
}

View File

@@ -17,7 +17,6 @@ using Eluant.ObjectBinding;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Scripting;
using OpenRA.Support;
using OpenRA.Traits;
using OpenRA.Widgets;
@@ -57,38 +56,23 @@ 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
{
get
{
// Players in mission maps must not leave the player view
return !inMissionMap && (spectating || WinState != WinState.Undefined);
}
}
public bool Spectating;
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
@@ -110,19 +94,19 @@ namespace OpenRA
readonly StanceColors stanceColors;
public static FactionInfo ResolveFaction(string factionName, IEnumerable<FactionInfo> factionInfos, MersenneTwister playerRandom, bool requireSelectable = true)
static FactionInfo ChooseFaction(World world, string name, bool requireSelectable = true)
{
var selectableFactions = factionInfos
var selectableFactions = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>()
.Where(f => !requireSelectable || f.Selectable)
.ToList();
var selected = selectableFactions.FirstOrDefault(f => f.InternalName == factionName)
?? selectableFactions.Random(playerRandom);
var selected = selectableFactions.FirstOrDefault(f => f.InternalName == name)
?? selectableFactions.Random(world.SharedRandom);
// Don't loop infinite
for (var i = 0; i <= 10 && selected.RandomFactionMembers.Any(); i++)
{
var faction = selected.RandomFactionMembers.Random(playerRandom);
var faction = selected.RandomFactionMembers.Random(world.SharedRandom);
selected = selectableFactions.FirstOrDefault(f => f.InternalName == faction);
if (selected == null)
@@ -132,32 +116,14 @@ namespace OpenRA
return selected;
}
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)
static FactionInfo ChooseDisplayFaction(World world, string factionName)
{
var factions = world.Map.Rules.Actors["world"].TraitInfos<FactionInfo>().ToArray();
return factions.FirstOrDefault(f => f.InternalName == factionName) ?? factions.First();
}
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)
public Player(World world, Session.Client client, PlayerReference pr)
{
World = world;
InternalName = pr.Name;
@@ -170,16 +136,18 @@ namespace OpenRA
{
ClientIndex = client.Index;
Color = client.Color;
PlayerName = ResolvePlayerName(client, world.LobbyInfo.Clients, world.Map.Rules.Actors["player"].TraitInfos<IBotInfo>());
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;
BotType = client.Bot;
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;
Faction = ChooseFaction(world, client.Faction, !pr.LockFaction);
DisplayFaction = ChooseDisplayFaction(world, client.Faction);
}
else
{
@@ -189,15 +157,13 @@ namespace OpenRA
PlayerName = pr.Name;
NonCombatant = pr.NonCombatant;
Playable = pr.Playable;
spectating = pr.Spectating;
Spectating = pr.Spectating;
BotType = pr.Bot;
Faction = ResolveFaction(world, pr.Faction, playerRandom, false);
DisplayFaction = ResolveDisplayFaction(world, pr.Faction);
HomeLocation = pr.HomeLocation;
SpawnPoint = DisplaySpawnPoint = 0;
Faction = ChooseFaction(world, pr.Faction, false);
DisplayFaction = ChooseDisplayFaction(world, pr.Faction);
}
if (!spectating)
if (!Spectating)
PlayerMask = new LongBitSet<PlayerBitMask>(InternalName);
// Set this property before running any Created callbacks on the player actor
@@ -238,27 +204,11 @@ namespace OpenRA
return "{0} ({1})".F(PlayerName, ClientIndex);
}
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 Dictionary<Player, Stance> Stances = new Dictionary<Player, Stance>();
public bool IsAlliedWith(Player p)
{
return RelationshipWith(p) == PlayerRelationship.Ally;
// Observers are considered allies to active combatants
return p == null || Stances[p] == Stance.Ally || (p.Spectating && !NonCombatant);
}
public Color PlayerStanceColor(Actor a)

View File

@@ -18,7 +18,6 @@ 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)
@@ -26,7 +25,6 @@ namespace OpenRA.Primitives
lock (Bits)
{
var bits = nextBits;
allBits |= bits;
nextBits <<= 1;
if (nextBits == 0)
@@ -87,8 +85,6 @@ 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.
@@ -118,7 +114,6 @@ 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); }

View File

@@ -9,7 +9,6 @@
*/
#endregion
using System;
using System.IO;
namespace OpenRA.Primitives
@@ -19,7 +18,7 @@ namespace OpenRA.Primitives
public Stream Stream1 { get; set; }
public Stream Stream2 { get; set; }
long VirtualLength { get; }
long VirtualLength { get; set; }
long position;
public MergedStream(Stream stream1, Stream stream2)
@@ -62,21 +61,7 @@ namespace OpenRA.Primitives
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int ReadByte()
{
int value;
if (position >= Stream1.Length)
value = Stream2.ReadByte();
else
value = Stream1.ReadByte();
position++;
return value;
VirtualLength = value;
}
public override int Read(byte[] buffer, int offset, int count)
@@ -98,14 +83,12 @@ namespace OpenRA.Primitives
return bytesRead;
}
public override void WriteByte(byte value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
if (position >= Stream1.Length)
Stream2.Write(buffer, offset - (int)Stream1.Length, count);
else
Stream1.Write(buffer, offset, count);
}
public override bool CanRead
@@ -120,7 +103,7 @@ namespace OpenRA.Primitives
public override bool CanWrite
{
get { return false; }
get { return Stream1.CanWrite && Stream2.CanWrite; }
}
public override long Length

View File

@@ -48,25 +48,9 @@ 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;
@@ -82,8 +66,7 @@ namespace OpenRA.Primitives
}
/// <summary>
/// Reads data into a buffer, which will be used to satisfy <see cref="ReadByte()"/> and
/// <see cref="Read(byte[], int, int)"/> calls.
/// Reads data into a buffer, which will be used to satisfy <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>

View File

@@ -59,35 +59,8 @@ namespace OpenRA.Primitives
set { BaseStream.Position = BaseOffset + value; }
}
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 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 void Flush() { BaseStream.Flush(); }
public override long Seek(long offset, SeekOrigin origin)
{

View File

@@ -17,7 +17,7 @@ namespace OpenRA
{
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1300:ElementMustBeginWithUpperCaseLetter", Justification = "Mimic a built-in type alias.")]
[StructLayout(LayoutKind.Sequential)]
public readonly struct float3 : IEquatable<float3>
public 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 +(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 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 Lerp(float3 a, float3 b, float t)
{
@@ -44,8 +44,8 @@ namespace OpenRA
float2.Lerp(a.Z, b.Z, t));
}
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 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 override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode(); }
public bool Equals(float3 other)

View File

@@ -2,19 +2,19 @@
"profiles": {
"Tiberian Dawn": {
"commandName": "Project",
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=cnc"
"commandLineArgs": "Game.Mod=cnc"
},
"Red Alert": {
"commandName": "Project",
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=ra"
"commandLineArgs": "Game.Mod=ra"
},
"Dune 2000": {
"commandName": "Project",
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=d2k"
"commandLineArgs": "Game.Mod=d2k"
},
"Tiberian Sun": {
"commandName": "Project",
"commandLineArgs": "Engine.EngineDir=\"..\" Game.Mod=ts"
"commandLineArgs": "Game.Mod=ts"
}
}
}
}

View File

@@ -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.DisableLegacyGL);
graphicSettings.VideoDisplay, graphicSettings.GLProfile);
Context = Window.Context;
@@ -431,15 +431,24 @@ namespace OpenRA
var srcWidth = screenSprite.Sheet.Size.Width;
var destWidth = screenSprite.Bounds.Width;
var destHeight = -screenSprite.Bounds.Height;
var channelOrder = new[] { 2, 1, 0, 3 };
ThreadPool.QueueUserWorkItem(_ =>
{
// Extract the screen rect from the (larger) backing surface
// Convert BGRA to RGBA
var dest = new byte[4 * destWidth * destHeight];
for (var y = 0; y < destHeight; y++)
Array.Copy(src, 4 * y * srcWidth, dest, 4 * y * destWidth, 4 * destWidth);
{
for (var x = 0; x < destWidth; x++)
{
var destOffset = 4 * (y * destWidth + x);
var srcOffset = 4 * (y * srcWidth + x);
for (var i = 0; i < 4; i++)
dest[destOffset + i] = src[srcOffset + channelOrder[i]];
}
}
new Png(dest, SpriteFrameType.Bgra32, destWidth, destHeight).Save(path);
new Png(dest, destWidth, destHeight).Save(path);
});
}

View File

@@ -161,8 +161,8 @@ namespace OpenRA.Scripting
.ToArray();
PlayerCommands = FilterCommands(world.Map.Rules.Actors["player"], knownPlayerCommands);
runtime.Globals["EngineDir"] = Platform.EngineDir;
runtime.DoBuffer(File.Open(Path.Combine(Platform.EngineDir, "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();
runtime.Globals["GameDir"] = Platform.GameDir;
runtime.DoBuffer(File.Open(Platform.ResolvePath(".", "lua", "scriptwrapper.lua"), FileMode.Open, FileAccess.Read).ReadAllText(), "scriptwrapper.lua").Dispose();
tick = (LuaFunction)runtime.Globals["Tick"];
// Register globals

View File

@@ -86,11 +86,9 @@ namespace OpenRA.Scripting
{
foreach (var arg in clrArgs)
{
var table = arg as LuaValue[];
if (table == null)
if (!(arg is LuaValue[]))
continue;
foreach (var value in table)
foreach (var value in (LuaValue[])arg)
value.Dispose();
}
}

View File

@@ -36,11 +36,11 @@ namespace OpenRA.Traits
if (a.Owner == viewer || viewer == null)
return basePriority;
switch (viewer.RelationshipWith(a.Owner))
switch (viewer.Stances[a.Owner])
{
case PlayerRelationship.Ally: return basePriority - PriorityRange;
case PlayerRelationship.Neutral: return basePriority - 2 * PriorityRange;
case PlayerRelationship.Enemy: return basePriority - 3 * PriorityRange;
case Stance.Ally: return basePriority - PriorityRange;
case Stance.Neutral: return basePriority - 2 * PriorityRange;
case Stance.Enemy: return basePriority - 3 * PriorityRange;
default:
throw new InvalidOperationException();

View File

@@ -25,8 +25,6 @@ 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
@@ -68,6 +66,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 = 11;
public const int Orders = 10;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -88,9 +88,6 @@ 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();
@@ -175,21 +172,20 @@ 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 (requires DisableLegacyGL: False)",
"Automatic: Use the first supported profile.")]
public GLProfile GLProfile = GLProfile.Automatic;
"Legacy: OpenGL 2.1 with framebuffer_object extension.")]
public GLProfile GLProfile = GLProfile.Modern;
public int BatchSize = 8192;
public int SheetSize = 2048;
public string Language = "english";
public string DefaultLanguage = "english";
}
public class SoundSettings

View File

@@ -61,22 +61,22 @@ namespace OpenRA
public static ushort ReadUInt16(this Stream s)
{
return (ushort)(s.ReadUInt8() | s.ReadUInt8() << 8);
return BitConverter.ToUInt16(s.ReadBytes(2), 0);
}
public static short ReadInt16(this Stream s)
{
return (short)(s.ReadUInt8() | s.ReadUInt8() << 8);
return BitConverter.ToInt16(s.ReadBytes(2), 0);
}
public static uint ReadUInt32(this Stream s)
{
return (uint)(s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24);
return BitConverter.ToUInt32(s.ReadBytes(4), 0);
}
public static int ReadInt32(this Stream s)
{
return s.ReadUInt8() | s.ReadUInt8() << 8 | s.ReadUInt8() << 16 | s.ReadUInt8() << 24;
return BitConverter.ToInt32(s.ReadBytes(4), 0);
}
public static void Write(this Stream s, int value)

View File

@@ -1,363 +0,0 @@
#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 and used under the terms of the Apache 2.0 license
#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

View File

@@ -21,4 +21,26 @@ 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;
}
}
}
}

View File

@@ -11,10 +11,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
@@ -124,16 +122,6 @@ 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);
@@ -289,32 +277,6 @@ 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;
}
}
}
}
}

View File

@@ -13,53 +13,54 @@ using System;
namespace OpenRA.Traits
{
public enum LintDictionaryReference
{
None = 0,
Keys = 1,
Values = 2
}
/* attributes used by OpenRA.Lint to understand the rules */
[AttributeUsage(AttributeTargets.Field)]
public sealed class ActorReferenceAttribute : Attribute
{
public readonly Type[] RequiredTraits;
public readonly LintDictionaryReference DictionaryReference;
public ActorReferenceAttribute(Type[] requiredTraits,
LintDictionaryReference dictionaryReference = LintDictionaryReference.None)
public Type[] RequiredTraits;
public ActorReferenceAttribute(params Type[] requiredTraits)
{
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
{
// The field name in the same trait info that contains the image name.
public readonly string ImageReference;
public readonly string ImageReference; // The field name in the same trait info that contains the image name.
public readonly bool Prefix;
public readonly bool AllowNullImage;
public readonly LintDictionaryReference DictionaryReference;
public SequenceReferenceAttribute(string imageReference = null, bool prefix = false, bool allowNullImage = false,
LintDictionaryReference dictionaryReference = LintDictionaryReference.None)
public readonly bool ActorNameFallback;
public SequenceReferenceAttribute(string imageReference = null, bool prefix = false, bool actorNameFallback = false)
{
ImageReference = imageReference;
Prefix = prefix;
AllowNullImage = allowNullImage;
DictionaryReference = dictionaryReference;
ActorNameFallback = actorNameFallback;
}
}

View File

@@ -39,11 +39,10 @@ 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 IEnumerable<WPos> TargetablePositions { get { return targetablePositions; } }
public WPos[] TargetablePositions { get; private set; }
public ITooltipInfo TooltipInfo { get; private set; }
public Player TooltipOwner { get; private set; }
@@ -118,8 +117,7 @@ namespace OpenRA.Traits
{
Owner = actor.Owner;
TargetTypes = actor.GetEnabledTargetTypes();
targetablePositions.Clear();
targetablePositions.AddRange(actor.GetTargetablePositions());
TargetablePositions = actor.GetTargetablePositions().ToArray();
Hidden = !actor.CanBeViewedByPlayer(viewer);
if (health != null)

View File

@@ -17,9 +17,11 @@ namespace OpenRA.Traits
[Desc("Required for shroud and fog visibility checks. Add this to the player actor.")]
public class ShroudInfo : TraitInfo, ILobbyOptions
{
[Translate]
[Desc("Descriptive label for the fog checkbox in the lobby.")]
public readonly string FogCheckboxLabel = "Fog of War";
[Translate]
[Desc("Tooltip description for the fog checkbox in the lobby.")]
public readonly string FogCheckboxDescription = "Line of sight is required to view enemy forces";
@@ -35,9 +37,11 @@ namespace OpenRA.Traits
[Desc("Display order for the fog checkbox in the lobby.")]
public readonly int FogCheckboxDisplayOrder = 0;
[Translate]
[Desc("Descriptive label for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxLabel = "Explored Map";
[Translate]
[Desc("Tooltip description for the explored map checkbox in the lobby.")]
public readonly string ExploredMapCheckboxDescription = "Initial map shroud is revealed";
@@ -95,7 +99,6 @@ 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;
@@ -139,7 +142,6 @@ 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);
@@ -157,42 +159,31 @@ namespace OpenRA.Traits
void ITick.Tick(Actor self)
{
if (!anyCellTouched)
return;
anyCellTouched = false;
if (OnShroudChanged == null)
return;
foreach (var puv in map.ProjectedCells)
{
var index = touched.Index(puv);
if (!touched[index])
if (!touched[puv])
continue;
touched[index] = false;
touched[puv] = false;
var type = ShroudCellType.Shroud;
if (explored[index])
if (explored[puv] && (!shroudGenerationEnabled || generatedShroudCount[puv] == 0 || visibleCount[puv] > 0))
{
var count = visibleCount[index];
if (!shroudGenerationEnabled || count > 0 || generatedShroudCount[index] == 0)
{
if (passiveVisibilityEnabled)
count += passiveVisibleCount[index];
var count = visibleCount[puv];
if (passiveVisibilityEnabled)
count += passiveVisibleCount[puv];
type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog;
}
type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog;
}
var oldResolvedType = resolvedType[index];
var oldResolvedType = resolvedType[puv];
resolvedType[puv] = type;
if (type != oldResolvedType)
{
resolvedType[index] = type;
OnShroudChanged(puv);
}
}
Hash = Sync.HashPlayer(self.Owner) + self.World.WorldTick;
@@ -240,23 +231,21 @@ namespace OpenRA.Traits
if (!map.Contains(puv))
continue;
var index = touched.Index(puv);
touched[index] = true;
anyCellTouched = true;
touched[puv] = true;
switch (type)
{
case SourceType.PassiveVisibility:
passiveVisibilityEnabled = true;
passiveVisibleCount[index]++;
explored[index] = true;
passiveVisibleCount[puv]++;
explored[puv] = true;
break;
case SourceType.Visibility:
visibleCount[index]++;
explored[index] = true;
visibleCount[puv]++;
explored[puv] = true;
break;
case SourceType.Shroud:
shroudGenerationEnabled = true;
generatedShroudCount[index]++;
generatedShroudCount[puv]++;
break;
}
}
@@ -272,19 +261,17 @@ namespace OpenRA.Traits
// Cells outside the visible bounds don't increment visibleCount
if (map.Contains(puv))
{
var index = touched.Index(puv);
touched[index] = true;
anyCellTouched = true;
touched[puv] = true;
switch (state.Type)
{
case SourceType.PassiveVisibility:
passiveVisibleCount[index]--;
passiveVisibleCount[puv]--;
break;
case SourceType.Visibility:
visibleCount[index]--;
visibleCount[puv]--;
break;
case SourceType.Shroud:
generatedShroudCount[index]--;
generatedShroudCount[puv]--;
break;
}
}
@@ -297,15 +284,10 @@ namespace OpenRA.Traits
{
foreach (var puv in cells)
{
if (map.Contains(puv))
if (map.Contains(puv) && !explored[puv])
{
var index = touched.Index(puv);
if (!explored[index])
{
touched[index] = true;
anyCellTouched = true;
explored[index] = true;
}
touched[puv] = true;
explored[puv] = true;
}
}
}
@@ -317,12 +299,10 @@ namespace OpenRA.Traits
foreach (var puv in map.ProjectedCells)
{
var index = touched.Index(puv);
if (!explored[index] && s.explored[index])
if (!explored[puv] && s.explored[puv])
{
touched[index] = true;
anyCellTouched = true;
explored[index] = true;
touched[puv] = true;
explored[puv] = true;
}
}
}
@@ -331,12 +311,10 @@ namespace OpenRA.Traits
{
foreach (var puv in map.ProjectedCells)
{
var index = touched.Index(puv);
if (!explored[index])
if (!explored[puv])
{
touched[index] = true;
anyCellTouched = true;
explored[index] = true;
touched[puv] = true;
explored[puv] = true;
}
}
}
@@ -345,12 +323,9 @@ namespace OpenRA.Traits
{
foreach (var puv in map.ProjectedCells)
{
var index = touched.Index(puv);
touched[index] = true;
explored[index] = (visibleCount[index] + passiveVisibleCount[index]) > 0;
touched[puv] = true;
explored[puv] = (visibleCount[puv] + passiveVisibleCount[puv]) > 0;
}
anyCellTouched = true;
}
public bool IsExplored(WPos pos)

View File

@@ -16,7 +16,7 @@ using System.Linq;
namespace OpenRA.Traits
{
public enum TargetType : byte { Invalid, Actor, Terrain, FrozenActor }
public readonly struct Target
public 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(in Target t) { return new Target(t.CenterPosition, t.Positions.ToArray()); }
public static Target FromTargetPositions(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); }

View File

@@ -18,7 +18,6 @@ using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA.Traits
{
@@ -68,7 +67,7 @@ namespace OpenRA.Traits
}
[Flags]
public enum PlayerRelationship
public enum Stance
{
None = 0,
Enemy = 1,
@@ -78,7 +77,7 @@ namespace OpenRA.Traits
public static class StanceExts
{
public static bool HasStance(this PlayerRelationship s, PlayerRelationship stance)
public static bool HasStance(this Stance s, Stance stance)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (s & stance) == stance;
@@ -128,7 +127,7 @@ namespace OpenRA.Traits
public interface IIssueOrder
{
IEnumerable<IOrderTargeter> Orders { get; }
Order IssueOrder(Actor self, IOrderTargeter order, in Target target, bool queued);
Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued);
}
[Flags]
@@ -147,9 +146,9 @@ namespace OpenRA.Traits
{
string OrderID { get; }
int OrderPriority { get; }
bool CanTarget(Actor self, in Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor);
bool CanTarget(Actor self, Target target, List<Actor> othersAtTarget, ref TargetModifiers modifiers, ref string cursor);
bool IsQueued { get; }
bool TargetOverridesSelection(Actor self, in Target target, List<Actor> actorsAt, CPos xy, TargetModifiers modifiers);
bool TargetOverridesSelection(Actor self, Target target, List<Actor> actorsAt, CPos xy, TargetModifiers modifiers);
}
public interface IResolveOrder { void ResolveOrder(Actor self, Order order); }
@@ -196,7 +195,7 @@ namespace OpenRA.Traits
public interface ITooltipInfo : ITraitInfoInterface
{
string TooltipForPlayerStance(PlayerRelationship stance);
string TooltipForPlayerStance(Stance stance);
bool IsOwnerRowVisible { get; }
}
@@ -268,7 +267,6 @@ 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
@@ -366,27 +364,7 @@ namespace OpenRA.Traits
}
[RequireExplicitImplementation]
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 ICreatePlayers { void CreatePlayers(World w); }
public interface IBotInfo : ITraitInfoInterface
{
@@ -507,10 +485,7 @@ namespace OpenRA.Traits
bool AlwaysEnabled { get; }
}
public interface IMoveInfo : ITraitInfoInterface
{
Color GetTargetLineColor();
}
public interface IMoveInfo : ITraitInfoInterface { }
[RequireExplicitImplementation]
public interface IGameOver { void GameOver(World world); }
@@ -520,7 +495,7 @@ namespace OpenRA.Traits
int Delay { get; }
bool IsValidAgainst(Actor victim, Actor firedBy);
bool IsValidAgainst(FrozenActor victim, Actor firedBy);
void DoImpact(in Target target, WarheadArgs args);
void DoImpact(Target target, WarheadArgs args);
}
public interface IRulesetLoaded<TInfo> { void RulesetLoaded(Ruleset rules, TInfo info); }
@@ -566,8 +541,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)
@@ -575,7 +550,7 @@ namespace OpenRA.Traits
public override string ValueChangedMessage(string playerName, string newValue)
{
return playerName + " " + BoolValues[newValue].ToLowerInvariant() + " " + Name + ".";
return playerName + " " + BoolValues[newValue] + " " + Name + ".";
}
}

View File

@@ -28,6 +28,7 @@ namespace OpenRA.Traits
[Desc("The side that the faction belongs to. For example, England belongs to the 'Allies' side.")]
public readonly string Side = null;
[Translate]
public readonly string Description = null;
public readonly bool Selectable = true;

View File

@@ -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], Enumerable.Empty<string>(), type);
new ExternalMods().Register(utility.ModData.Manifest, args[1], type);
}
}
}

View File

@@ -16,7 +16,7 @@ namespace OpenRA
/// <summary>
/// 3d World rotation.
/// </summary>
public readonly struct WRot : IEquatable<WRot>
public 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 +(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 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 WRot Rotate(in WRot rot)
public WRot Rotate(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 ==(in WRot me, in WRot other)
public static bool operator ==(WRot me, WRot other)
{
return me.Roll == other.Roll && me.Pitch == other.Pitch && me.Yaw == other.Yaw;
}
public static bool operator !=(in WRot me, in WRot other) { return !(me == other); }
public static bool operator !=(WRot me, WRot other) { return !(me == other); }
public WRot WithRoll(WAngle roll)
{

View File

@@ -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(in WRot rot)
public WVec Rotate(WRot rot)
{
rot.AsMatrix(out var mtx);
return Rotate(ref mtx);

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