Merge tag 'v0.7.0-RC10'
This commit is contained in:
commit
91e579bdd9
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: gitsubmodule
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
36
.github/labeler.yml
vendored
36
.github/labeler.yml
vendored
@ -1,36 +0,0 @@
|
|||||||
"part: game client":
|
|
||||||
- src/Components/Modules/Stats*
|
|
||||||
|
|
||||||
"part: game server":
|
|
||||||
- src/Components/Modules/Dedicated*
|
|
||||||
|
|
||||||
"part: zonebuilder":
|
|
||||||
- src/Components/Modules/ZoneBuilder*
|
|
||||||
|
|
||||||
"area: menus":
|
|
||||||
- src/Components/Modules/Menus*
|
|
||||||
|
|
||||||
"area: anticheat":
|
|
||||||
- src/Components/Modules/AntiCheat*
|
|
||||||
|
|
||||||
"area: serverlist":
|
|
||||||
- src/Components/Modules/ServerList*
|
|
||||||
|
|
||||||
"area: weapons":
|
|
||||||
- src/Components/Modules/Weapon*
|
|
||||||
|
|
||||||
"area: networking":
|
|
||||||
- src/Components/Modules/Auth*
|
|
||||||
- src/Components/Modules/Network*
|
|
||||||
- src/Components/Modules/Node*
|
|
||||||
- src/Components/Modules/PlayerName*
|
|
||||||
- src/Components/Modules/RCon*
|
|
||||||
- src/Components/Modules/Session*
|
|
||||||
|
|
||||||
"area: continuous integration":
|
|
||||||
- "*appveyor*"
|
|
||||||
- ".github/workflows/**"
|
|
||||||
- Jenkinsfile
|
|
||||||
|
|
||||||
"status: work in progress":
|
|
||||||
- "**"
|
|
117
.github/workflows/build.yml
vendored
Normal file
117
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
types: [opened, synchronize, reopened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build binaries
|
||||||
|
runs-on: windows-2022
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
configuration:
|
||||||
|
- Debug
|
||||||
|
- Release
|
||||||
|
steps:
|
||||||
|
- name: Wait for previous workflows
|
||||||
|
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
|
||||||
|
uses: softprops/turnstyle@v1
|
||||||
|
with:
|
||||||
|
poll-interval-seconds: 10
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Check out files
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
fetch-depth: 0
|
||||||
|
# NOTE - If LFS ever starts getting used during builds, switch this to true!
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
- name: Add msbuild to PATH
|
||||||
|
uses: microsoft/setup-msbuild@v1.1
|
||||||
|
|
||||||
|
- name: Generate project files
|
||||||
|
run: tools/premake5 vs2022 --ac-disable
|
||||||
|
|
||||||
|
- name: Set up problem matching
|
||||||
|
uses: ammaraskar/msvc-problem-matcher@master
|
||||||
|
|
||||||
|
- name: Build ${{matrix.configuration}} binaries
|
||||||
|
run: msbuild /m /v:minimal /p:Configuration=${{matrix.configuration}} /p:Platform=Win32 build/iw4x.sln
|
||||||
|
|
||||||
|
- name: Upload ${{matrix.configuration}} binaries
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: ${{matrix.configuration}} binaries
|
||||||
|
path: |
|
||||||
|
build/bin/Win32/${{matrix.configuration}}/iw4x.dll
|
||||||
|
build/bin/Win32/${{matrix.configuration}}/iw4x.pdb
|
||||||
|
|
||||||
|
# - name: Upload ${{matrix.configuration}} data artifacts
|
||||||
|
# uses: actions/upload-artifact@v2
|
||||||
|
# with:
|
||||||
|
# name: ${{matrix.configuration}} data artifacts
|
||||||
|
# path: |
|
||||||
|
# data/*
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
name: Deploy artifacts
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || github.ref == 'refs/heads/develop')
|
||||||
|
steps:
|
||||||
|
- name: Setup main environment
|
||||||
|
if: github.ref == 'refs/heads/master'
|
||||||
|
run: echo "XLABS_MASTER_PATH=${{ secrets.XLABS_MASTER_SSH_PATH }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Setup develop environment
|
||||||
|
if: github.ref == 'refs/heads/develop'
|
||||||
|
run: echo "XLABS_MASTER_PATH=${{ secrets.XLABS_MASTER_SSH_PATH_DEV }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Download Release binaries
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
with:
|
||||||
|
name: Release binaries
|
||||||
|
|
||||||
|
# - name: Download Release data artifacts
|
||||||
|
# uses: actions/download-artifact@v2
|
||||||
|
# with:
|
||||||
|
# name: Release data artifacts
|
||||||
|
# path: data
|
||||||
|
|
||||||
|
# Set up committer info and GPG key
|
||||||
|
- name: Install SSH key
|
||||||
|
uses: shimataro/ssh-key-action@v2
|
||||||
|
with:
|
||||||
|
key: ${{ secrets.XLABS_MASTER_SSH_PRIVATE_KEY }}
|
||||||
|
known_hosts: 'just-a-placeholder-so-we-dont-get-errors'
|
||||||
|
|
||||||
|
- name: Add known hosts
|
||||||
|
run: ssh-keyscan -H ${{ secrets.XLABS_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts
|
||||||
|
|
||||||
|
- name: Wait for previous workflows
|
||||||
|
uses: softprops/turnstyle@v1
|
||||||
|
with:
|
||||||
|
poll-interval-seconds: 10
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# - name: Remove old data files
|
||||||
|
# run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} rm -rf ${{ env.XLABS_MASTER_PATH }}/iw4x/data/*
|
||||||
|
|
||||||
|
- name: Upload iw4x binary
|
||||||
|
run: rsync -avz iw4x.dll ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }}:${{ env.XLABS_MASTER_PATH }}/iw4x/
|
||||||
|
|
||||||
|
# - name: Upload data files
|
||||||
|
# run: rsync -avz ./data/ ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }}:${{ env.XLABS_MASTER_PATH }}/iw4x/data/
|
||||||
|
|
||||||
|
- name: Publish changes
|
||||||
|
run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} ${{ secrets.XLABS_MASTER_SSH_CHANGE_PUBLISH_COMMAND }}
|
17
.github/workflows/discord-notify.yml
vendored
Normal file
17
.github/workflows/discord-notify.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Notify Discord
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
issues:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
notify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository_owner == 'XLabsProject'
|
||||||
|
steps:
|
||||||
|
- name: Send notification to Discord
|
||||||
|
uses: Ilshidur/action-discord@master
|
||||||
|
env:
|
||||||
|
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}
|
48
.github/workflows/draft-new-release.yml
vendored
Normal file
48
.github/workflows/draft-new-release.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name: "Draft new release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: "The version you want to release."
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
draft-new-release:
|
||||||
|
name: "Draft a new release"
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Normalize version
|
||||||
|
id: normalize_version
|
||||||
|
run: |
|
||||||
|
version="${{ github.event.inputs.version }}"
|
||||||
|
version="v${version#v}"
|
||||||
|
echo "::set-output name=version::$version"
|
||||||
|
|
||||||
|
# Set up committer info and GPG key
|
||||||
|
- name: Import GPG key
|
||||||
|
id: import_gpg
|
||||||
|
uses: XLabsProject/ghaction-import-gpg@25d9d6ab99eb355c169c33c2306a72df85d9f516
|
||||||
|
with:
|
||||||
|
git-commit-gpgsign: true
|
||||||
|
git-committer-email: "${{ secrets.XLABS_CI_EMAIL }}"
|
||||||
|
git-committer-name: "${{ secrets.XLABS_CI_NAME }}"
|
||||||
|
# git-push-gpgsign: true
|
||||||
|
git-tag-gpgsign: true
|
||||||
|
git-user-signingkey: true
|
||||||
|
gpg-private-key: ${{ secrets.XLABS_CI_GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.XLABS_CI_GPG_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.XLABS_CI_GITHUB_TOKEN }}
|
||||||
|
source_branch: "develop"
|
||||||
|
destination_branch: "master"
|
||||||
|
pr_allow_empty: true
|
||||||
|
pr_body: |
|
||||||
|
This Pull Request is for the release of IW4x ${{ steps.normalize_version.outputs.version }} and was [automatically created by a workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) triggered by @${{ github.actor }}.
|
||||||
|
pr_title: Release ${{ steps.normalize_version.outputs.version }}
|
||||||
|
pr_label: release
|
11
.github/workflows/labeler.yml
vendored
11
.github/workflows/labeler.yml
vendored
@ -1,11 +0,0 @@
|
|||||||
name: "Pull Request Labeler"
|
|
||||||
on:
|
|
||||||
- pull_request
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
labeler:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/labeler@v2
|
|
||||||
with:
|
|
||||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
|
83
.github/workflows/release.yml
vendored
Normal file
83
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
types: [closed]
|
||||||
|
jobs:
|
||||||
|
merge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: Merge Release
|
||||||
|
steps:
|
||||||
|
- name: Check out files
|
||||||
|
if: github.event.pull_request.merged
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: false
|
||||||
|
lfs: false
|
||||||
|
|
||||||
|
# Set up committer info and GPG key
|
||||||
|
- name: Import GPG key
|
||||||
|
if: github.event.pull_request.merged
|
||||||
|
id: import_gpg
|
||||||
|
uses: XLabsProject/ghaction-import-gpg@25d9d6ab99eb355c169c33c2306a72df85d9f516
|
||||||
|
with:
|
||||||
|
git-commit-gpgsign: true
|
||||||
|
git-committer-email: "${{ secrets.XLABS_CI_EMAIL }}"
|
||||||
|
git-committer-name: "${{ secrets.XLABS_CI_NAME }}"
|
||||||
|
git-push-gpgsign: false
|
||||||
|
git-tag-gpgsign: true
|
||||||
|
git-user-signingkey: true
|
||||||
|
gpg-private-key: ${{ secrets.XLABS_CI_GPG_PRIVATE_KEY }}
|
||||||
|
passphrase: ${{ secrets.XLABS_CI_GPG_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Extract version from pull request
|
||||||
|
if: github.event.pull_request.merged
|
||||||
|
id: extract_version
|
||||||
|
run: |
|
||||||
|
title="${{ github.event.pull_request.title }}"
|
||||||
|
version="${title#Release }"
|
||||||
|
echo "::set-output name=version::$version"
|
||||||
|
|
||||||
|
- name: Create annotated tag
|
||||||
|
if: github.event.pull_request.merged
|
||||||
|
run: |
|
||||||
|
git tag -a -m "${{ github.event.pull_request.title }}" \
|
||||||
|
"${{ steps.extract_version.outputs.version }}" \
|
||||||
|
"${{ github.event.pull_request.merge_commit_sha }}"
|
||||||
|
git push origin --tags
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
if: github.event.pull_request.merged
|
||||||
|
uses: repo-sync/pull-request@v2
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.XLABS_CI_GITHUB_TOKEN }}
|
||||||
|
source_branch: "master"
|
||||||
|
destination_branch: "develop"
|
||||||
|
pr_allow_empty: true
|
||||||
|
pr_body: |
|
||||||
|
This Pull Request merges the release of IW4x ${{ steps.extract_version.outputs.version }} and was [automatically created by a workflow](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) triggered by @${{ github.actor }}.
|
||||||
|
pr_title: Merge release ${{ steps.extract_version.outputs.version }}
|
||||||
|
pr_label: release
|
||||||
|
|
||||||
|
|
||||||
|
notify:
|
||||||
|
name: Notify Discord
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: |
|
||||||
|
github.repository_owner == 'XLabsProject' && (
|
||||||
|
(
|
||||||
|
github.event.pull_request.merged
|
||||||
|
) || (
|
||||||
|
github.event.push.ref == 'refs/heads/master' ||
|
||||||
|
github.event.push.ref == 'refs/heads/develop'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
steps:
|
||||||
|
- name: Post CI status notification to Discord
|
||||||
|
uses: sarisia/actions-status-discord@v1.7.1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
webhook: ${{ secrets.DISCORD_CI_BOT_WEBHOOK }}
|
||||||
|
title: "Build"
|
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -25,7 +25,7 @@
|
|||||||
[submodule "deps/protobuf"]
|
[submodule "deps/protobuf"]
|
||||||
path = deps/protobuf
|
path = deps/protobuf
|
||||||
url = https://github.com/google/protobuf.git
|
url = https://github.com/google/protobuf.git
|
||||||
branch = 3.11.x
|
branch = 3.17.x
|
||||||
[submodule "deps/udis86"]
|
[submodule "deps/udis86"]
|
||||||
path = deps/udis86
|
path = deps/udis86
|
||||||
url = https://github.com/vmt/udis86.git
|
url = https://github.com/vmt/udis86.git
|
||||||
|
317
Jenkinsfile
vendored
317
Jenkinsfile
vendored
@ -1,317 +0,0 @@
|
|||||||
#!groovy
|
|
||||||
|
|
||||||
/*
|
|
||||||
This is our new pipeline script to do all of the building in, of and around IW4x.
|
|
||||||
|
|
||||||
Here's what it is supposed to do:
|
|
||||||
|
|
||||||
- Make sure Modern Warfare 2 is installed (CI should provide the folder like a custom tool)
|
|
||||||
- Check out code from iw4x-data
|
|
||||||
- Build the IW4x client library (this code repository)
|
|
||||||
- Use iw4x.exe from the iw4x-data repository in order to build the zone files in iw4x-data
|
|
||||||
- Package the IW4x client with the newly built data files
|
|
||||||
|
|
||||||
At this point it is done building everything, however afterwards we want the build server to
|
|
||||||
also push the newly built files to an update repository, depending on the branch we're on.
|
|
||||||
|
|
||||||
- For "develop", release to the "iw4x-dev" branch on the repository server.
|
|
||||||
- For "master", release to the "iw4x" branch on the repository server.
|
|
||||||
|
|
||||||
I'm looking into how the logic of pipelining works in detail before deciding on whether to
|
|
||||||
throw in the IW4x Updater and the IW4x Node binaries in as well or not.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note that this is just a rewrite of the jobs as they are currently set up on the production
|
|
||||||
Jenkins server. This will allow every developer to tinker around with how the build process
|
|
||||||
is set up. For those who want to play around with this, here's a bit of information:
|
|
||||||
|
|
||||||
- This is a Groovy script. Essentially Java but with less bullshit (like brackets and verbose writing).
|
|
||||||
- This gets directly translated into a Jenkins pipeline.
|
|
||||||
- If you have no idea how to handle scripts, get your hands off this file.
|
|
||||||
- If you do not use Jenkins, get your hands off this file.
|
|
||||||
- If you fuck this script up, I will kill you.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import groovy.transform.Field
|
|
||||||
|
|
||||||
@Field def configurations = [
|
|
||||||
"Debug": [
|
|
||||||
WorkspaceID: "build@debug",
|
|
||||||
StashName: "iw4x-debug",
|
|
||||||
MSBuildConfiguration: "Debug",
|
|
||||||
PremakeArgs: "",
|
|
||||||
Archive: true,
|
|
||||||
],
|
|
||||||
"Release": [
|
|
||||||
WorkspaceID: "build@release",
|
|
||||||
StashName: "iw4x-release",
|
|
||||||
MSBuildConfiguration: "Release",
|
|
||||||
PremakeArgs: "",
|
|
||||||
Archive: true,
|
|
||||||
],
|
|
||||||
"Release with unit tests": [
|
|
||||||
WorkspaceID: "build@release+unittests",
|
|
||||||
StashName: "iw4x-release-unittests",
|
|
||||||
MSBuildConfiguration: "Release",
|
|
||||||
PremakeArgs: "--force-unit-tests",
|
|
||||||
Archive: false,
|
|
||||||
],
|
|
||||||
].collect {k, v -> [k, v]}
|
|
||||||
|
|
||||||
@Field def testing = [
|
|
||||||
"Debug": [
|
|
||||||
WorkspaceID: "testing@debug",
|
|
||||||
StashName: "iw4x-debug",
|
|
||||||
],
|
|
||||||
"Release": [
|
|
||||||
WorkspaceID: "testing@release",
|
|
||||||
StashName: "iw4x-release-unittests",
|
|
||||||
],
|
|
||||||
].collect {k, v -> [k, v]}
|
|
||||||
|
|
||||||
def jobWorkspace(id, f) {
|
|
||||||
ws("workspace/${env.JOB_NAME.replaceAll(/[%$]/, "_")}@$id", f)
|
|
||||||
}
|
|
||||||
|
|
||||||
def useShippedPremake(f) {
|
|
||||||
def premakeHome = "${pwd()}\\tools"
|
|
||||||
|
|
||||||
withEnv(["PATH+=${premakeHome}"], f)
|
|
||||||
}
|
|
||||||
|
|
||||||
def getIW4xExecutable() {
|
|
||||||
step([
|
|
||||||
$class: 'CopyArtifact',
|
|
||||||
filter: '*',
|
|
||||||
fingerprintArtifacts: true,
|
|
||||||
projectName: 'iw4x/iw4x-executable/' + iw4xExecutableBranch(),
|
|
||||||
selector: [
|
|
||||||
$class: 'TriggeredBuildSelector',
|
|
||||||
allowUpstreamDependencies: false,
|
|
||||||
fallbackToLastSuccessful: true,
|
|
||||||
upstreamFilterStrategy: 'UseGlobalSetting'
|
|
||||||
]
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will build the IW4x client.
|
|
||||||
// We need a Windows Server with Visual Studio 2015, Premake5 and Git on it.
|
|
||||||
def doBuild(cfg) {
|
|
||||||
retry(5) {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
|
|
||||||
useShippedPremake {
|
|
||||||
def outputDir = pwd()
|
|
||||||
def msbuild = tool "Microsoft.NET MSBuild 15.0"
|
|
||||||
bat "premake5 vs2017 ${cfg.PremakeArgs}"
|
|
||||||
bat "\"${msbuild}\" build\\iw4x.sln \"/p:OutDir=$outputDir\\\\\" \"/p:Configuration=${cfg.MSBuildConfiguration}\""
|
|
||||||
}
|
|
||||||
|
|
||||||
stash name: "${cfg.StashName}", includes: "*.dll,*.pdb"
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will run the unit tests for IW4x.
|
|
||||||
// We need a Windows Server with MW2 on it.
|
|
||||||
def doUnitTests(name) {
|
|
||||||
mw2dir = tool "Modern Warfare 2"
|
|
||||||
|
|
||||||
unstash "$name"
|
|
||||||
|
|
||||||
// Get installed localization for correct zonefiles directory junction
|
|
||||||
def localization = readFile("${tool "Modern Warfare 2"}/localization.txt").split("\r?\n")[0]
|
|
||||||
|
|
||||||
try {
|
|
||||||
timeout(time: 10, unit: "MINUTES") {
|
|
||||||
// Set up environment
|
|
||||||
if (isUnix()) {
|
|
||||||
def mw2dir = tool "Modern Warfare 2"
|
|
||||||
sh """
|
|
||||||
mkdir -p zone
|
|
||||||
for f in main zone/dlc \"zone/$localization\"; do
|
|
||||||
ln -sfv \"$mw2dir/\$f\" \"\$f\"
|
|
||||||
done
|
|
||||||
for f in \"$mw2dir\"/*.dll \"$mw2dir\"/*.txt \"$mw2dir\"/*.bmp; do
|
|
||||||
ln -sfv \"\$f\" \"\$(basename \"\$f\")\"
|
|
||||||
done
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
def mw2dir = tool "Modern Warfare 2"
|
|
||||||
bat """
|
|
||||||
mklink /J \"main\" \"$mw2dir\\main\"
|
|
||||||
mkdir \"zone\"
|
|
||||||
mklink /J \"zone\\dlc\" \"$mw2dir\\zone\\dlc\"
|
|
||||||
mklink /J \"zone\\$localization\" \"$mw2dir\\zone\\$localization\"
|
|
||||||
copy /y \"$mw2dir\\*.dll\"
|
|
||||||
copy /y \"$mw2dir\\*.txt\"
|
|
||||||
copy /y \"$mw2dir\\*.bmp\"
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run tests
|
|
||||||
getIW4xExecutable()
|
|
||||||
retry(2) {
|
|
||||||
if (isUnix()) {
|
|
||||||
sh "WINEDEBUG=warn+all wine iw4x.exe -tests; wineserver -w"
|
|
||||||
} else {
|
|
||||||
bat "iw4x.exe -tests"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (org.jenkinsci.plugins.workflow.steps.FlowInterruptedException e) {
|
|
||||||
currentBuild.result = 'UNSTABLE'
|
|
||||||
println("${name} unit test interrupted (ran too long?)")
|
|
||||||
} catch (Exception e) {
|
|
||||||
println("${name} unit test failed.")
|
|
||||||
if (isUnix()) {
|
|
||||||
currentBuild.result = 'UNSTABLE'
|
|
||||||
} else {
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// In all cases make sure to at least remove the directory junctions!
|
|
||||||
if (!isUnix()) {
|
|
||||||
bat """
|
|
||||||
rmdir \"main\"
|
|
||||||
rmdir \"zone\\dlc\"
|
|
||||||
rmdir \"zone\\$localization\"
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the IW4x executable branch to use
|
|
||||||
def iw4xExecutableBranch() {
|
|
||||||
try {
|
|
||||||
return IW4X_EXECUTABLE_BRANCH;
|
|
||||||
} catch(MissingPropertyException) {
|
|
||||||
return "master";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Job properties
|
|
||||||
properties([
|
|
||||||
buildDiscarder(logRotator(artifactDaysToKeepStr: '', artifactNumToKeepStr: '', daysToKeepStr: '', numToKeepStr: '30')),
|
|
||||||
disableConcurrentBuilds(),
|
|
||||||
gitLabConnection('iw4x'),
|
|
||||||
[$class: 'LeastLoadDisabledProperty', leastLoadDisabled: false]
|
|
||||||
])
|
|
||||||
|
|
||||||
gitlabBuilds(builds: ["Checkout & Versioning", "Build", "Testing", "Archiving"]) {
|
|
||||||
// First though let's give this build a proper name
|
|
||||||
stage("Checkout & Versioning") {
|
|
||||||
gitlabCommitStatus(name: "Checkout & Versioning") {
|
|
||||||
node("windows") {
|
|
||||||
jobWorkspace("versioning") {
|
|
||||||
if (env.BRANCH_NAME == 'master')
|
|
||||||
{
|
|
||||||
echo 'Reset build environment'
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
|
|
||||||
retry(5) {
|
|
||||||
checkout scm
|
|
||||||
}
|
|
||||||
|
|
||||||
useShippedPremake {
|
|
||||||
def version = bat(returnStdout: true, script: '@premake5 version').split("\r?\n")[2]
|
|
||||||
|
|
||||||
currentBuild.setDisplayName "$version (#${env.BUILD_NUMBER})"
|
|
||||||
}
|
|
||||||
|
|
||||||
stash name: "jenkins-files", includes: "jenkins/**"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each available configuration generate a normal build and a unit test build.
|
|
||||||
stage("Build") {
|
|
||||||
gitlabCommitStatus(name: "Build") {
|
|
||||||
def executions = [:]
|
|
||||||
for (int i = 0; i < configurations.size(); i++) {
|
|
||||||
def entry = configurations[i]
|
|
||||||
|
|
||||||
def configName = entry[0]
|
|
||||||
def config = entry[1]
|
|
||||||
|
|
||||||
executions[configName] = {
|
|
||||||
node("windows") {
|
|
||||||
jobWorkspace(config.WorkspaceID) {
|
|
||||||
doBuild(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parallel executions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run unit tests on each configuration.
|
|
||||||
stage("Testing") {
|
|
||||||
gitlabCommitStatus(name: "Testing") {
|
|
||||||
executions = [:]
|
|
||||||
for (int i = 0; i < testing.size(); i++) {
|
|
||||||
def entry = testing.get(i)
|
|
||||||
|
|
||||||
def testName = entry[0]
|
|
||||||
def test = entry[1]
|
|
||||||
|
|
||||||
executions["$testName on Windows"] = {
|
|
||||||
node("windows") {
|
|
||||||
jobWorkspace(test.WorkspaceID) {
|
|
||||||
doUnitTests(test.StashName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executions["$testName on Linux"] = {
|
|
||||||
node("docker && linux && amd64") {
|
|
||||||
timeout(time: 10, unit: "MINUTES") {
|
|
||||||
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
|
|
||||||
def image = null
|
|
||||||
dir("src") {
|
|
||||||
unstash "jenkins-files"
|
|
||||||
image = docker.build("github.com/IW4x/iw4x-client-testing-wine32", "--rm --force-rm -f jenkins/wine32.Dockerfile jenkins")
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
image.inside {
|
|
||||||
doUnitTests(test.StashName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parallel executions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all the binaries and give each configuration its own subfolder
|
|
||||||
stage("Archiving") {
|
|
||||||
gitlabCommitStatus(name: "Archiving") {
|
|
||||||
node("windows") { // any node will do
|
|
||||||
jobWorkspace("archiving") {
|
|
||||||
try {
|
|
||||||
for (int i = 0; i < configurations.size(); i++) {
|
|
||||||
def entry = configurations[i]
|
|
||||||
|
|
||||||
def configName = entry[0]
|
|
||||||
def config = entry[1]
|
|
||||||
|
|
||||||
if (config.Archive) {
|
|
||||||
dir(configName) {
|
|
||||||
unstash config.StashName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
archiveArtifacts artifacts: "**/*.dll,**/*.pdb", fingerprint: true
|
|
||||||
} finally {
|
|
||||||
deleteDir()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
41
README.md
41
README.md
@ -2,28 +2,16 @@
|
|||||||
![forks](https://img.shields.io/github/forks/IW4x/iw4x-client.svg)
|
![forks](https://img.shields.io/github/forks/IW4x/iw4x-client.svg)
|
||||||
![stars](https://img.shields.io/github/stars/IW4x/iw4x-client.svg)
|
![stars](https://img.shields.io/github/stars/IW4x/iw4x-client.svg)
|
||||||
![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg)
|
![issues](https://img.shields.io/github/issues/IW4x/iw4x-client.svg)
|
||||||
[![build status](https://ci.appveyor.com/api/projects/status/rvljq0ooxen0oexm/branch/develop?svg=true)](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop)
|
[![build](https://github.com/XLabsProject/iw4x-client/workflows/Build/badge.svg)](https://github.com/XLabsProject/iw4x-client/actions)
|
||||||
[![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3)
|
[![discord](https://img.shields.io/endpoint?url=https://momo5502.com/iw4x/members-badge.php)](https://discord.gg/sKeVmR3)
|
||||||
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/iw4x)
|
[![patreon](https://img.shields.io/badge/patreon-support-blue.svg?logo=patreon)](https://www.patreon.com/xlabsproject)
|
||||||
|
|
||||||
# IW4x: Client
|
# IW4x: Client
|
||||||
|
|
||||||
## Commit message style
|
|
||||||
|
|
||||||
```
|
|
||||||
[Module] Imperative summary
|
|
||||||
|
|
||||||
- points or text
|
|
||||||
|
|
||||||
[ci skip]
|
|
||||||
```
|
|
||||||
|
|
||||||
`[ci skip]` is optional.
|
|
||||||
|
|
||||||
## How to compile
|
## How to compile
|
||||||
|
|
||||||
- Run `premake5 vs2019` or use the delivered `generate.bat`.
|
- Run `premake5 vs2022` or use the delivered `generate.bat`.
|
||||||
- Build via solution file in `build\iw4x.sln`. (You can use the `build.bat` script to do it quick and easy.)
|
- Build via solution file in `build\iw4x.sln`.
|
||||||
|
|
||||||
## Premake arguments
|
## Premake arguments
|
||||||
|
|
||||||
@ -40,6 +28,27 @@
|
|||||||
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
||||||
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
||||||
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
||||||
|
| `--iw4x-zones` | Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches. |
|
||||||
|
|
||||||
|
## Command line arguments
|
||||||
|
|
||||||
|
| Argument | Description |
|
||||||
|
|:------------------------|:-----------------------------------------------|
|
||||||
|
| `-tests` | Perform unit tests. |
|
||||||
|
| `-entries` | Print to the console a list of every asset as they are loaded from zonefiles. |
|
||||||
|
| `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. |
|
||||||
|
| `-console` | Allow the game to display its own separate interactive console window. |
|
||||||
|
| `-dedicated` | Starts the game as a headless dedicated server. |
|
||||||
|
| `-scriptablehttp` | Enable HTTP related gsc functions. |
|
||||||
|
| `-bigminidumps` | Include all code sections from loaded modules in the dump. |
|
||||||
|
| `-reallybigminidumps` | Include data sections from all loaded modules in the dump. |
|
||||||
|
| `-dump` | Write info of loaded assets to the raw folder as they are being loaded. |
|
||||||
|
| `-monitor` | This flag is for internal use and it is used to indicate if an external console is present. |
|
||||||
|
| `-nointro` | Skip game's cinematic intro. |
|
||||||
|
| `-version` | Print IW4x build info on startup. |
|
||||||
|
| `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. |
|
||||||
|
| `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. |
|
||||||
|
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|
||||||
|
34
appveyor.yml
34
appveyor.yml
@ -1,34 +0,0 @@
|
|||||||
# AppVeyor CI configuration
|
|
||||||
|
|
||||||
version: "#{build} ({branch})"
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
|
||||||
PREMAKE_ACTION: vs2019
|
|
||||||
|
|
||||||
configuration:
|
|
||||||
- Debug
|
|
||||||
- Release
|
|
||||||
|
|
||||||
platform: Win32
|
|
||||||
|
|
||||||
install:
|
|
||||||
- ps: |
|
|
||||||
Write-Host "Updating version information..." -ForegroundColor Cyan
|
|
||||||
|
|
||||||
Update-AppveyorBuild -Version $(& tools/premake5.exe version | select -Last 1)
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
- ps: |
|
|
||||||
Write-Host "Generating project files with premake..." -ForegroundColor Cyan
|
|
||||||
& "./tools/premake5.exe" $env:PREMAKE_ACTION
|
|
||||||
Write-Host "Generated" -ForegroundColor Green
|
|
||||||
|
|
||||||
build:
|
|
||||||
project: build/iw4x.sln
|
|
||||||
parallel: true
|
|
||||||
verbosity: minimal
|
|
||||||
|
|
||||||
artifacts:
|
|
||||||
- path: build/bin/**/*.dll
|
|
||||||
- path: build/bin/**/*.pdb
|
|
24
build.bat
24
build.bat
@ -1,24 +0,0 @@
|
|||||||
@echo off & setlocal
|
|
||||||
|
|
||||||
cd %~dp0
|
|
||||||
|
|
||||||
if exist "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat" call "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsMSBuildCmd.bat"
|
|
||||||
call msbuild /version >NUL 2>NUL
|
|
||||||
if errorlevel 0 goto:build
|
|
||||||
|
|
||||||
if exist "%PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\15.0\Bin\msbuild.exe" path %PROGRAMFILES(x86)%\Microsoft Visual Studio\2019\Enterprise\MSBuild\15.0\Bin;%PATH%
|
|
||||||
call msbuild /version >NUL 2>NUL
|
|
||||||
if errorlevel 0 goto:build
|
|
||||||
|
|
||||||
echo Couldn't find any MSBuild to build this project.
|
|
||||||
echo Make sure you have Visual C++ Build Tools 2019 or Visual Studio 2019 installed.
|
|
||||||
endlocal
|
|
||||||
exit /B 1
|
|
||||||
|
|
||||||
:build
|
|
||||||
call generate.bat
|
|
||||||
set PLATFORM=Win32
|
|
||||||
set CONFIGURATION=Release
|
|
||||||
call msbuild /nologo /m /v:m %* build\iw4x.sln
|
|
||||||
endlocal
|
|
||||||
exit /B %ERRORLEVEL%
|
|
2
deps/json11
vendored
2
deps/json11
vendored
@ -1 +1 @@
|
|||||||
Subproject commit e2e3a11e99672b018e0e0657867e6a3439e180cf
|
Subproject commit 2df9473fb3605980db55ecddf34392a2e832ad35
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 1937f412605e1b04ddb41ef9c2f2f0aab7e61548
|
Subproject commit 06a81aeb227424182125363f7554fad5146d6d2a
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 6ac0b0c1b69b9a88e1b3b3002c2e3a9062ae99b4
|
Subproject commit 5108f12350b6daa4aa5dbc846517ad1db2f8388a
|
2
deps/pdcurses
vendored
2
deps/pdcurses
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 618e0aaa31b4728eb4df78ec4de6c2b873908eda
|
Subproject commit 2fa0f10dd844da47ee83c05a40a1ec541ceb95e1
|
19
deps/premake/dxsdk.lua
vendored
Normal file
19
deps/premake/dxsdk.lua
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
dxsdk = {
|
||||||
|
source = path.join(dependencies.basePath, "dxsdk"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function dxsdk.import()
|
||||||
|
libdirs {path.join(dxsdk.source, "Lib/x86")}
|
||||||
|
dxsdk.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function dxsdk.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(dxsdk.source, "Include"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function dxsdk.project()
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, dxsdk)
|
32
deps/premake/json11.lua
vendored
Normal file
32
deps/premake/json11.lua
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
json11 = {
|
||||||
|
source = path.join(dependencies.basePath, "json11"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function json11.import()
|
||||||
|
links {"json11"}
|
||||||
|
|
||||||
|
json11.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function json11.includes()
|
||||||
|
includedirs {json11.source}
|
||||||
|
end
|
||||||
|
|
||||||
|
function json11.project()
|
||||||
|
project "json11"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
files
|
||||||
|
{
|
||||||
|
path.join(json11.source, "*.cpp"),
|
||||||
|
path.join(json11.source, "*.hpp"),
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
|
||||||
|
defines {"_LIB"}
|
||||||
|
removedefines {"_USRDLL", "_DLL"}
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, json11)
|
59
deps/premake/libtomcrypt.lua
vendored
Normal file
59
deps/premake/libtomcrypt.lua
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
libtomcrypt = {
|
||||||
|
source = path.join(dependencies.basePath, "libtomcrypt"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function libtomcrypt.import()
|
||||||
|
links {"libtomcrypt"}
|
||||||
|
|
||||||
|
libtomcrypt.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function libtomcrypt.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(libtomcrypt.source, "src/headers")
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"LTC_NO_FAST",
|
||||||
|
"LTC_NO_PROTOTYPES",
|
||||||
|
"LTC_NO_RSA_BLINDING",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function libtomcrypt.project()
|
||||||
|
project "libtomcrypt"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
libtomcrypt.includes()
|
||||||
|
libtommath.import()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(libtomcrypt.source, "src/**.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
removefiles {
|
||||||
|
path.join(libtomcrypt.source, "src/**/*tab.c"),
|
||||||
|
path.join(libtomcrypt.source, "src/encauth/ocb3/**.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_WARNINGS",
|
||||||
|
"LTC_SOURCE",
|
||||||
|
"_LIB",
|
||||||
|
"USE_LTM"
|
||||||
|
}
|
||||||
|
|
||||||
|
removedefines {
|
||||||
|
"_DLL",
|
||||||
|
"_USRDLL"
|
||||||
|
}
|
||||||
|
|
||||||
|
linkoptions {
|
||||||
|
"-IGNORE:4221"
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, libtomcrypt)
|
50
deps/premake/libtommath.lua
vendored
Normal file
50
deps/premake/libtommath.lua
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
libtommath = {
|
||||||
|
source = path.join(dependencies.basePath, "libtommath"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function libtommath.import()
|
||||||
|
links {"libtommath"}
|
||||||
|
|
||||||
|
libtommath.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function libtommath.includes()
|
||||||
|
includedirs {
|
||||||
|
libtommath.source
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"LTM_DESC",
|
||||||
|
"__STDC_IEC_559__",
|
||||||
|
"MP_NO_DEV_URANDOM",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function libtommath.project()
|
||||||
|
project "libtommath"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
libtommath.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(libtommath.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_LIB"
|
||||||
|
}
|
||||||
|
|
||||||
|
removedefines {
|
||||||
|
"_DLL",
|
||||||
|
"_USRDLL"
|
||||||
|
}
|
||||||
|
|
||||||
|
linkoptions {
|
||||||
|
"-IGNORE:4221"
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, libtommath)
|
43
deps/premake/minizip.lua
vendored
Normal file
43
deps/premake/minizip.lua
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
minizip = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib/contrib/minizip"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function minizip.import()
|
||||||
|
links {"minizip"}
|
||||||
|
zlib.import()
|
||||||
|
minizip.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.includes()
|
||||||
|
includedirs {
|
||||||
|
minizip.source
|
||||||
|
}
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function minizip.project()
|
||||||
|
project "minizip"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
minizip.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(minizip.source, "*.h"),
|
||||||
|
path.join(minizip.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
removefiles {
|
||||||
|
path.join(minizip.source, "miniunz.c"),
|
||||||
|
path.join(minizip.source, "minizip.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, minizip)
|
32
deps/premake/mongoose.lua
vendored
Normal file
32
deps/premake/mongoose.lua
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
mongoose = {
|
||||||
|
source = path.join(dependencies.basePath, "mongoose"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function mongoose.import()
|
||||||
|
links {"mongoose"}
|
||||||
|
|
||||||
|
mongoose.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mongoose.includes()
|
||||||
|
includedirs {mongoose.source}
|
||||||
|
end
|
||||||
|
|
||||||
|
function mongoose.project()
|
||||||
|
project "mongoose"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
mongoose.includes()
|
||||||
|
|
||||||
|
files
|
||||||
|
{
|
||||||
|
path.join(mongoose.source, "*.c"),
|
||||||
|
path.join(mongoose.source, "*.h"),
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, mongoose)
|
34
deps/premake/pdcurses.lua
vendored
Normal file
34
deps/premake/pdcurses.lua
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
pdcurses = {
|
||||||
|
source = path.join(dependencies.basePath, "pdcurses"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function pdcurses.import()
|
||||||
|
links {"pdcurses"}
|
||||||
|
|
||||||
|
pdcurses.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function pdcurses.includes()
|
||||||
|
includedirs {pdcurses.source}
|
||||||
|
end
|
||||||
|
|
||||||
|
function pdcurses.project()
|
||||||
|
project "pdcurses"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
pdcurses.includes()
|
||||||
|
|
||||||
|
files
|
||||||
|
{
|
||||||
|
path.join(pdcurses.source, "pdcurses/*.c"),
|
||||||
|
path.join(pdcurses.source, "pdcurses/*.h"),
|
||||||
|
path.join(pdcurses.source, "wincon/*.c"),
|
||||||
|
path.join(pdcurses.source, "wincon/*.h"),
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, pdcurses)
|
50
deps/premake/protobuf.lua
vendored
Normal file
50
deps/premake/protobuf.lua
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
protobuf = {
|
||||||
|
source = path.join(dependencies.basePath, "protobuf"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function protobuf.import()
|
||||||
|
links {"protobuf"}
|
||||||
|
|
||||||
|
protobuf.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function protobuf.includes()
|
||||||
|
includedirs {
|
||||||
|
path.join(protobuf.source, "src"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function protobuf.project()
|
||||||
|
project "protobuf"
|
||||||
|
language "C++"
|
||||||
|
|
||||||
|
protobuf.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(protobuf.source, "src/**.cc"),
|
||||||
|
"./src/**.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
removefiles {
|
||||||
|
path.join(protobuf.source, "src/**/*test.cc"),
|
||||||
|
path.join(protobuf.source, "src/google/protobuf/*test*.cc"),
|
||||||
|
|
||||||
|
path.join(protobuf.source, "src/google/protobuf/testing/**.cc"),
|
||||||
|
path.join(protobuf.source, "src/google/protobuf/compiler/**.cc"),
|
||||||
|
|
||||||
|
path.join(protobuf.source, "src/google/protobuf/arena_nc.cc"),
|
||||||
|
path.join(protobuf.source, "src/google/protobuf/util/internal/error_listener.cc"),
|
||||||
|
path.join(protobuf.source, "**/*_gcc.cc"),
|
||||||
|
}
|
||||||
|
|
||||||
|
rules {"ProtobufCompiler"}
|
||||||
|
|
||||||
|
defines {"_SCL_SECURE_NO_WARNINGS"}
|
||||||
|
|
||||||
|
linkoptions {"-IGNORE:4221"}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, protobuf)
|
35
deps/premake/udis86.lua
vendored
Normal file
35
deps/premake/udis86.lua
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
udis86 = {
|
||||||
|
source = path.join(dependencies.basePath, "udis86"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function udis86.import()
|
||||||
|
links {"udis86"}
|
||||||
|
|
||||||
|
udis86.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function udis86.includes()
|
||||||
|
includedirs {
|
||||||
|
udis86.source,
|
||||||
|
path.join(udis86.source, "libudis86"),
|
||||||
|
path.join(dependencies.basePath, "extra/udis86"),
|
||||||
|
path.join(dependencies.basePath, "extra/udis86/libudis86"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function udis86.project()
|
||||||
|
project "udis86"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
udis86.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(udis86.source, "libudis86/*.c"),
|
||||||
|
path.join(dependencies.basePath, "extra/udis86/libudis86/*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, udis86)
|
39
deps/premake/zlib.lua
vendored
Normal file
39
deps/premake/zlib.lua
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
zlib = {
|
||||||
|
source = path.join(dependencies.basePath, "zlib"),
|
||||||
|
}
|
||||||
|
|
||||||
|
function zlib.import()
|
||||||
|
links {"zlib"}
|
||||||
|
zlib.includes()
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.includes()
|
||||||
|
includedirs {
|
||||||
|
zlib.source
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"ZLIB_CONST",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function zlib.project()
|
||||||
|
project "zlib"
|
||||||
|
language "C"
|
||||||
|
|
||||||
|
zlib.includes()
|
||||||
|
|
||||||
|
files {
|
||||||
|
path.join(zlib.source, "*.h"),
|
||||||
|
path.join(zlib.source, "*.c"),
|
||||||
|
}
|
||||||
|
|
||||||
|
defines {
|
||||||
|
"_CRT_SECURE_NO_DEPRECATE",
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings "Off"
|
||||||
|
kind "StaticLib"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(dependencies, zlib)
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 63cfdafacba6141717a2df97fc123dc0c14ba7c4
|
Subproject commit 5500c72c5b616da9f0125bcfab513987a1226e2b
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
|||||||
Subproject commit d71dc66fa8a153fb6e7c626847095d9697a6cf42
|
Subproject commit ec3df00224d4b396e2ac6586ab5d25f673caa4c2
|
@ -1,4 +1,4 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo Updating submodules...
|
echo Updating submodules...
|
||||||
call git submodule update --init --recursive
|
call git submodule update --init --recursive
|
||||||
call tools\premake5 %* vs2019
|
call tools\premake5 %* vs2022 --ac-disable
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
# Requires a decent modern Docker version (v1.10.x at least ideally)
|
|
||||||
|
|
||||||
# Use semi-official Arch Linux image with fixed versioning
|
|
||||||
FROM archlinux/base
|
|
||||||
|
|
||||||
# Environment variables
|
|
||||||
ENV WINEPREFIX /wine32
|
|
||||||
ENV WINEARCH win32
|
|
||||||
ENV WINEDEBUG -all
|
|
||||||
|
|
||||||
# Install Wine (32-bit)
|
|
||||||
RUN \
|
|
||||||
echo -e "#!/bin/sh\nwine \$@\nretval=\$?\nwineserver -w\nexit \$retval" > /usr/local/bin/wine-wrapper &&\
|
|
||||||
chmod +x /usr/local/bin/wine-wrapper &&\
|
|
||||||
\
|
|
||||||
(\
|
|
||||||
echo '' &&\
|
|
||||||
echo '[multilib]' &&\
|
|
||||||
echo 'Include = /etc/pacman.d/mirrorlist'\
|
|
||||||
) >> /etc/pacman.conf &&\
|
|
||||||
pacman -Sy --noconfirm \
|
|
||||||
awk \
|
|
||||||
lib32-gnutls \
|
|
||||||
wine \
|
|
||||||
wget \
|
|
||||||
xorg-server-xvfb \
|
|
||||||
pacman-contrib \
|
|
||||||
awk \
|
|
||||||
&&\
|
|
||||||
\
|
|
||||||
wine-wrapper wineboot.exe -i &&\
|
|
||||||
wget -Ovcredist_x86.exe https://download.microsoft.com/download/d/d/9/dd9a82d0-52ef-40db-8dab-795376989c03/vcredist_x86.exe &&\
|
|
||||||
WINEDEBUG=+all-trace xvfb-run sh -c 'wine-wrapper vcredist_x86.exe /q' &&\
|
|
||||||
rm vcredist_x86.exe &&\
|
|
||||||
\
|
|
||||||
pacman -Rs --noconfirm \
|
|
||||||
xorg-server-xvfb \
|
|
||||||
wget \
|
|
||||||
&&\
|
|
||||||
\
|
|
||||||
find /. -name "*~" -type f -delete &&\
|
|
||||||
rm -rf /tmp/* /var/tmp/* /usr/share/man/* /usr/share/info/* /usr/share/doc/* &&\
|
|
||||||
pacman -Scc --noconfirm &&\
|
|
||||||
rm -rf /var/lib/pacman/sync/*
|
|
||||||
|
|
||||||
USER 0
|
|
5011
lib/include/stb_truetype.h
Normal file
5011
lib/include/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,35 +0,0 @@
|
|||||||
dxsdk = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function dxsdk.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
dxsdk.settings = settings
|
|
||||||
|
|
||||||
if not dxsdk.settings.defines then dxsdk.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function dxsdk.import()
|
|
||||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
|
||||||
|
|
||||||
--filter "platforms:*32"
|
|
||||||
libdirs { path.join(dxsdk.settings.source, "Lib/x86") }
|
|
||||||
|
|
||||||
--filter "platforms:*64"
|
|
||||||
-- libdirs { path.join(dxsdk.settings.source, "Lib/x64") }
|
|
||||||
|
|
||||||
--filter {}
|
|
||||||
|
|
||||||
dxsdk.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function dxsdk.includes()
|
|
||||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
|
||||||
|
|
||||||
includedirs { path.join(dxsdk.settings.source, "Include") }
|
|
||||||
defines(dxsdk.settings.defines)
|
|
||||||
end
|
|
||||||
|
|
||||||
function dxsdk.project()
|
|
||||||
end
|
|
@ -1,54 +0,0 @@
|
|||||||
iw4mvm = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function iw4mvm.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
iw4mvm.settings = settings
|
|
||||||
|
|
||||||
if not iw4mvm.settings.defines then iw4mvm.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function iw4mvm.import()
|
|
||||||
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
|
|
||||||
|
|
||||||
links { "iw4mvm" }
|
|
||||||
iw4mvm.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function iw4mvm.includes()
|
|
||||||
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
|
|
||||||
|
|
||||||
includedirs { iw4mvm.settings.source }
|
|
||||||
libdirs { path.join(iw4mvm.settings.source, "IW4MVM") }
|
|
||||||
defines(iw4mvm.settings.defines)
|
|
||||||
end
|
|
||||||
|
|
||||||
function iw4mvm.project()
|
|
||||||
if not iw4mvm.settings then error("You need to call iw4mvm.setup first") end
|
|
||||||
|
|
||||||
project "iw4mvm"
|
|
||||||
language "C++"
|
|
||||||
|
|
||||||
characterset ("MBCS")
|
|
||||||
|
|
||||||
defines("_CRT_SECURE_NO_WARNINGS")
|
|
||||||
|
|
||||||
iw4mvm.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(iw4mvm.settings.source, "IW4MVM/*.h"),
|
|
||||||
path.join(iw4mvm.settings.source, "IW4MVM/*.cpp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
removefiles
|
|
||||||
{
|
|
||||||
--path.join(iw4mvm.settings.source, "IW4MVM/detours.cpp"),
|
|
||||||
path.join(iw4mvm.settings.source, "IW4MVM/DllMain.cpp"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,51 +0,0 @@
|
|||||||
json11 = {
|
|
||||||
settings = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
function json11.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
json11.settings = settings
|
|
||||||
end
|
|
||||||
|
|
||||||
function json11.import()
|
|
||||||
if not json11.settings then error("Run json11.setup first") end
|
|
||||||
|
|
||||||
links { "json11" }
|
|
||||||
json11.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function json11.includes()
|
|
||||||
if not json11.settings then error("Run json11.setup first") end
|
|
||||||
|
|
||||||
includedirs { json11.settings.source }
|
|
||||||
end
|
|
||||||
|
|
||||||
function json11.project()
|
|
||||||
if not json11.settings then error("Run json11.setup first") end
|
|
||||||
|
|
||||||
project "json11"
|
|
||||||
language "C++"
|
|
||||||
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
json11.settings.source,
|
|
||||||
}
|
|
||||||
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(json11.settings.source, "*.cpp"),
|
|
||||||
path.join(json11.settings.source, "*.hpp"),
|
|
||||||
}
|
|
||||||
removefiles
|
|
||||||
{
|
|
||||||
path.join(json11.settings.source, "test*"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
defines { "_LIB" }
|
|
||||||
removedefines { "_USRDLL", "_DLL" }
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,62 +0,0 @@
|
|||||||
libtomcrypt = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function libtomcrypt.setup(settings)
|
|
||||||
if not settings.source then error("Missing source") end
|
|
||||||
|
|
||||||
libtomcrypt.settings = settings
|
|
||||||
|
|
||||||
if not libtomcrypt.settings.defines then libtomcrypt.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtomcrypt.import()
|
|
||||||
if not libtomcrypt.settings then error("Run libtomcrypt.setup first") end
|
|
||||||
|
|
||||||
links { "libtomcrypt" }
|
|
||||||
libtomcrypt.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtomcrypt.includes()
|
|
||||||
if not libtomcrypt.settings then error("Run libtomcrypt.setup first") end
|
|
||||||
|
|
||||||
defines(libtomcrypt.settings.defines)
|
|
||||||
includedirs { path.join(libtomcrypt.settings.source, "src/headers") }
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtomcrypt.project()
|
|
||||||
if not libtomcrypt.settings then error("Run libtomcrypt.setup first") end
|
|
||||||
|
|
||||||
project "libtomcrypt"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
libtomcrypt.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(libtomcrypt.settings.source, "src/**.c"),
|
|
||||||
}
|
|
||||||
removefiles
|
|
||||||
{
|
|
||||||
path.join(libtomcrypt.settings.source, "src/**/*tab.c"), -- included by files as necessary already afaik
|
|
||||||
path.join(libtomcrypt.settings.source, "src/encauth/ocb3/**.c"), -- fails in Visual Studio with invalid syntax
|
|
||||||
}
|
|
||||||
defines
|
|
||||||
{
|
|
||||||
"_CRT_SECURE_NO_WARNINGS",
|
|
||||||
"LTC_SOURCE", -- we are compiling from source code
|
|
||||||
}
|
|
||||||
|
|
||||||
-- dependencies
|
|
||||||
if libtommath and libtommath.settings then
|
|
||||||
defines { "USE_LTM" }
|
|
||||||
libtommath.import()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
defines { "_LIB" }
|
|
||||||
removedefines { "_DLL", "_USRDLL" }
|
|
||||||
linkoptions { "-IGNORE:4221" }
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,46 +0,0 @@
|
|||||||
libtommath = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function libtommath.setup(settings)
|
|
||||||
if not settings.source then error("Missing source") end
|
|
||||||
|
|
||||||
libtommath.settings = settings
|
|
||||||
|
|
||||||
if not libtommath.settings.defines then libtommath.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtommath.import()
|
|
||||||
if not libtommath.settings then error("Run libtommath.setup first") end
|
|
||||||
|
|
||||||
links { "libtommath" }
|
|
||||||
libtommath.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtommath.includes()
|
|
||||||
if not libtommath.settings then error("Run libtommath.setup first") end
|
|
||||||
|
|
||||||
defines(libtommath.settings.defines)
|
|
||||||
includedirs { libtommath.settings.source }
|
|
||||||
end
|
|
||||||
|
|
||||||
function libtommath.project()
|
|
||||||
if not libtommath.settings then error("Run libtommath.setup first") end
|
|
||||||
|
|
||||||
project "libtommath"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
libtommath.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(libtommath.settings.source, "*.c"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
defines { "_LIB" }
|
|
||||||
removedefines { "_DLL", "_USRDLL" }
|
|
||||||
linkoptions { "-IGNORE:4221" }
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,42 +0,0 @@
|
|||||||
mongoose = {
|
|
||||||
settings = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
function mongoose.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
mongoose.settings = settings
|
|
||||||
end
|
|
||||||
|
|
||||||
function mongoose.import()
|
|
||||||
if not mongoose.settings then error("Run mongoose.setup first") end
|
|
||||||
|
|
||||||
links { "mongoose" }
|
|
||||||
mongoose.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function mongoose.includes()
|
|
||||||
if not mongoose.settings then error("Run mongoose.setup first") end
|
|
||||||
|
|
||||||
includedirs { mongoose.settings.source }
|
|
||||||
end
|
|
||||||
|
|
||||||
function mongoose.project()
|
|
||||||
if not mongoose.settings then error("Run mongoose.setup first") end
|
|
||||||
|
|
||||||
project "mongoose"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
mongoose.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(mongoose.settings.source, "*.c"),
|
|
||||||
path.join(mongoose.settings.source, "*.h"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
-- always build as static lib, as mongoose doesn't export anything
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,48 +0,0 @@
|
|||||||
pdcurses = {
|
|
||||||
settings = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
function pdcurses.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
pdcurses.settings = settings
|
|
||||||
end
|
|
||||||
|
|
||||||
function pdcurses.import()
|
|
||||||
if not pdcurses.settings then error("Run pdcurses.setup first") end
|
|
||||||
|
|
||||||
links { "pdcurses" }
|
|
||||||
pdcurses.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function pdcurses.includes()
|
|
||||||
if not pdcurses.settings then error("Run pdcurses.setup first") end
|
|
||||||
|
|
||||||
includedirs { pdcurses.settings.source }
|
|
||||||
end
|
|
||||||
|
|
||||||
function pdcurses.project()
|
|
||||||
if not pdcurses.settings then error("Run pdcurses.setup first") end
|
|
||||||
|
|
||||||
project "pdcurses"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
pdcurses.settings.source,
|
|
||||||
}
|
|
||||||
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(pdcurses.settings.source, "pdcurses/*.c"),
|
|
||||||
path.join(pdcurses.settings.source, "pdcurses/*.h"),
|
|
||||||
path.join(pdcurses.settings.source, "wincon/*.c"),
|
|
||||||
path.join(pdcurses.settings.source, "wincon/*.h"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
-- always build as static lib, as pdcurses doesn't export anything
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,68 +0,0 @@
|
|||||||
protobuf = {
|
|
||||||
settings = nil,
|
|
||||||
}
|
|
||||||
|
|
||||||
function protobuf.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
protobuf.settings = settings
|
|
||||||
end
|
|
||||||
|
|
||||||
function protobuf.import()
|
|
||||||
if not protobuf.settings then error("Run protobuf.setup first") end
|
|
||||||
|
|
||||||
links { "protobuf" }
|
|
||||||
protobuf.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function protobuf.includes()
|
|
||||||
if not protobuf.settings then error("Run protobuf.setup first") end
|
|
||||||
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
path.join(protobuf.settings.source, "src"),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function protobuf.project()
|
|
||||||
if not protobuf.settings then error("Run protobuf.setup first") end
|
|
||||||
|
|
||||||
project "protobuf"
|
|
||||||
language "C++"
|
|
||||||
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
path.join(protobuf.settings.source, "src"),
|
|
||||||
}
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(protobuf.settings.source, "src/**.cc"),
|
|
||||||
"./src/**.proto",
|
|
||||||
}
|
|
||||||
removefiles
|
|
||||||
{
|
|
||||||
path.join(protobuf.settings.source, "src/**/*test.cc"),
|
|
||||||
path.join(protobuf.settings.source, "src/google/protobuf/*test*.cc"),
|
|
||||||
|
|
||||||
path.join(protobuf.settings.source, "src/google/protobuf/testing/**.cc"),
|
|
||||||
path.join(protobuf.settings.source, "src/google/protobuf/compiler/**.cc"),
|
|
||||||
|
|
||||||
path.join(protobuf.settings.source, "src/google/protobuf/arena_nc.cc"),
|
|
||||||
path.join(protobuf.settings.source, "src/google/protobuf/util/internal/error_listener.cc"),
|
|
||||||
path.join(protobuf.settings.source, "**/*_gcc.cc"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Generate source code from protobuf definitions
|
|
||||||
rules { "ProtobufCompiler" }
|
|
||||||
|
|
||||||
-- dependencies
|
|
||||||
zlib.import()
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
defines { "_SCL_SECURE_NO_WARNINGS" }
|
|
||||||
warnings "Off"
|
|
||||||
linkoptions { "-IGNORE:4221" }
|
|
||||||
|
|
||||||
-- always build as static lib, as we include our custom classes and therefore can't perform shared linking
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,51 +0,0 @@
|
|||||||
udis86 = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function udis86.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
udis86.settings = settings
|
|
||||||
|
|
||||||
if not udis86.settings.defines then udis86.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function udis86.import()
|
|
||||||
if not udis86.settings then error("You need to call udis86.setup first") end
|
|
||||||
|
|
||||||
links { "udis86" }
|
|
||||||
udis86.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function udis86.includes()
|
|
||||||
if not udis86.settings then error("You need to call udis86.setup first") end
|
|
||||||
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
udis86.settings.source,
|
|
||||||
path.join(udis86.settings.source, "libudis86/"),
|
|
||||||
path.join(udis86.settings.source, "../extra/udis86/"),
|
|
||||||
path.join(udis86.settings.source, "../extra/udis86/libudis86/")
|
|
||||||
}
|
|
||||||
defines(udis86.settings.defines)
|
|
||||||
end
|
|
||||||
|
|
||||||
function udis86.project()
|
|
||||||
if not udis86.settings then error("You need to call udis86.setup first") end
|
|
||||||
|
|
||||||
project "udis86"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
udis86.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(udis86.settings.source, "libudis86/*.h"),
|
|
||||||
path.join(udis86.settings.source, "libudis86/*.c"),
|
|
||||||
path.join(udis86.settings.source, "../extra/udis86/libudis86/*.c"),
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
@ -1,48 +0,0 @@
|
|||||||
zlib = {
|
|
||||||
settings = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
function zlib.setup(settings)
|
|
||||||
if not settings.source then error("Missing source.") end
|
|
||||||
|
|
||||||
zlib.settings = settings
|
|
||||||
|
|
||||||
if not zlib.settings.defines then zlib.settings.defines = {} end
|
|
||||||
end
|
|
||||||
|
|
||||||
function zlib.import()
|
|
||||||
if not zlib.settings then error("You need to call zlib.setup first") end
|
|
||||||
|
|
||||||
links { "zlib" }
|
|
||||||
zlib.includes()
|
|
||||||
end
|
|
||||||
|
|
||||||
function zlib.includes()
|
|
||||||
if not zlib.settings then error("You need to call zlib.setup first") end
|
|
||||||
|
|
||||||
includedirs { zlib.settings.source }
|
|
||||||
defines(zlib.settings.defines)
|
|
||||||
end
|
|
||||||
|
|
||||||
function zlib.project()
|
|
||||||
if not zlib.settings then error("You need to call zlib.setup first") end
|
|
||||||
|
|
||||||
project "zlib"
|
|
||||||
language "C"
|
|
||||||
|
|
||||||
zlib.includes()
|
|
||||||
files
|
|
||||||
{
|
|
||||||
path.join(zlib.settings.source, "*.h"),
|
|
||||||
path.join(zlib.settings.source, "*.c"),
|
|
||||||
}
|
|
||||||
defines
|
|
||||||
{
|
|
||||||
"_CRT_SECURE_NO_DEPRECATE",
|
|
||||||
}
|
|
||||||
|
|
||||||
-- not our code, ignore POSIX usage warnings for now
|
|
||||||
warnings "Off"
|
|
||||||
|
|
||||||
kind "StaticLib"
|
|
||||||
end
|
|
266
premake5.lua
266
premake5.lua
@ -3,6 +3,9 @@ gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"
|
|||||||
|
|
||||||
-- Quote the given string input as a C string
|
-- Quote the given string input as a C string
|
||||||
function cstrquote(value)
|
function cstrquote(value)
|
||||||
|
if value == nil then
|
||||||
|
return "\"\""
|
||||||
|
end
|
||||||
result = value:gsub("\\", "\\\\")
|
result = value:gsub("\\", "\\\\")
|
||||||
result = result:gsub("\"", "\\\"")
|
result = result:gsub("\"", "\\\"")
|
||||||
result = result:gsub("\n", "\\n")
|
result = result:gsub("\n", "\\n")
|
||||||
@ -27,18 +30,42 @@ function vertonumarr(value, vernumber)
|
|||||||
return vernum
|
return vernum
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Option to allow copying the DLL file to a custom folder after build
|
dependencies = {
|
||||||
|
basePath = "./deps"
|
||||||
|
}
|
||||||
|
|
||||||
|
function dependencies.load()
|
||||||
|
dir = path.join(dependencies.basePath, "premake/*.lua")
|
||||||
|
deps = os.matchfiles(dir)
|
||||||
|
|
||||||
|
for i, dep in pairs(deps) do
|
||||||
|
dep = dep:gsub(".lua", "")
|
||||||
|
require(dep)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function dependencies.imports()
|
||||||
|
for i, proj in pairs(dependencies) do
|
||||||
|
if type(i) == 'number' then
|
||||||
|
proj.import()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function dependencies.projects()
|
||||||
|
for i, proj in pairs(dependencies) do
|
||||||
|
if type(i) == 'number' then
|
||||||
|
proj.project()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
newoption {
|
newoption {
|
||||||
trigger = "copy-to",
|
trigger = "copy-to",
|
||||||
description = "Optional, copy the DLL to a custom folder after build, define the path here if wanted.",
|
description = "Optional, copy the DLL to a custom folder after build, define the path here if wanted.",
|
||||||
value = "PATH"
|
value = "PATH"
|
||||||
}
|
}
|
||||||
|
|
||||||
newoption {
|
|
||||||
trigger = "no-new-structure",
|
|
||||||
description = "Do not use new virtual path structure (separating headers and source files)."
|
|
||||||
}
|
|
||||||
|
|
||||||
newoption {
|
newoption {
|
||||||
trigger = "copy-pdb",
|
trigger = "copy-pdb",
|
||||||
description = "Copy debug information for binaries as well to the path given via --copy-to."
|
description = "Copy debug information for binaries as well to the path given via --copy-to."
|
||||||
@ -74,6 +101,11 @@ newoption {
|
|||||||
description = "Upload minidumps even for Debug builds."
|
description = "Upload minidumps even for Debug builds."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
newoption {
|
||||||
|
trigger = "iw4x-zones",
|
||||||
|
description = "Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches."
|
||||||
|
}
|
||||||
|
|
||||||
newaction {
|
newaction {
|
||||||
trigger = "version",
|
trigger = "version",
|
||||||
description = "Returns the version string for the current commit of the source code.",
|
description = "Returns the version string for the current commit of the source code.",
|
||||||
@ -178,111 +210,51 @@ newaction {
|
|||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
depsBasePath = "./deps"
|
dependencies.load()
|
||||||
|
|
||||||
require "premake/json11"
|
|
||||||
require "premake/libtomcrypt"
|
|
||||||
require "premake/libtommath"
|
|
||||||
require "premake/mongoose"
|
|
||||||
require "premake/pdcurses"
|
|
||||||
require "premake/protobuf"
|
|
||||||
require "premake/zlib"
|
|
||||||
require "premake/udis86"
|
|
||||||
require "premake/iw4mvm"
|
|
||||||
require "premake/dxsdk"
|
|
||||||
|
|
||||||
json11.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "json11"),
|
|
||||||
}
|
|
||||||
libtomcrypt.setup
|
|
||||||
{
|
|
||||||
defines = {
|
|
||||||
"LTC_NO_FAST",
|
|
||||||
"LTC_NO_PROTOTYPES",
|
|
||||||
"LTC_NO_RSA_BLINDING",
|
|
||||||
},
|
|
||||||
source = path.join(depsBasePath, "libtomcrypt"),
|
|
||||||
}
|
|
||||||
libtommath.setup
|
|
||||||
{
|
|
||||||
defines = {
|
|
||||||
"LTM_DESC",
|
|
||||||
"__STDC_IEC_559__",
|
|
||||||
},
|
|
||||||
source = path.join(depsBasePath, "libtommath"),
|
|
||||||
}
|
|
||||||
mongoose.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "mongoose"),
|
|
||||||
}
|
|
||||||
pdcurses.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "pdcurses"),
|
|
||||||
}
|
|
||||||
protobuf.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "protobuf"),
|
|
||||||
}
|
|
||||||
zlib.setup
|
|
||||||
{
|
|
||||||
defines = {
|
|
||||||
"ZLIB_CONST"
|
|
||||||
},
|
|
||||||
source = path.join(depsBasePath, "zlib"),
|
|
||||||
}
|
|
||||||
udis86.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "udis86"),
|
|
||||||
}
|
|
||||||
iw4mvm.setup
|
|
||||||
{
|
|
||||||
defines = {
|
|
||||||
"IW4X",
|
|
||||||
"DETOURS_X86",
|
|
||||||
"DETOURS_32BIT",
|
|
||||||
},
|
|
||||||
source = path.join(depsBasePath, "iw4mvm"),
|
|
||||||
}
|
|
||||||
dxsdk.setup
|
|
||||||
{
|
|
||||||
source = path.join(depsBasePath, "dxsdk"),
|
|
||||||
}
|
|
||||||
|
|
||||||
workspace "iw4x"
|
workspace "iw4x"
|
||||||
startproject "iw4x"
|
startproject "iw4x"
|
||||||
location "./build"
|
location "./build"
|
||||||
objdir "%{wks.location}/obj"
|
objdir "%{wks.location}/obj"
|
||||||
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||||
buildlog "%{wks.location}/obj/%{cfg.architecture}/%{cfg.buildcfg}/%{prj.name}/%{prj.name}.log"
|
|
||||||
configurations {"Debug", "Release"}
|
configurations {"Debug", "Release"}
|
||||||
|
|
||||||
|
language "C++"
|
||||||
|
cppdialect "C++17"
|
||||||
|
|
||||||
architecture "x86"
|
architecture "x86"
|
||||||
platforms "x86"
|
platforms "Win32"
|
||||||
--exceptionhandling ("SEH")
|
|
||||||
|
|
||||||
|
systemversion "latest"
|
||||||
|
symbols "On"
|
||||||
staticruntime "On"
|
staticruntime "On"
|
||||||
|
editandcontinue "Off"
|
||||||
|
warnings "Extra"
|
||||||
|
characterset "ASCII"
|
||||||
|
|
||||||
configuration "windows"
|
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
|
||||||
|
|
||||||
|
filter "platforms:Win*"
|
||||||
defines {"_WINDOWS", "WIN32"}
|
defines {"_WINDOWS", "WIN32"}
|
||||||
|
filter {}
|
||||||
|
|
||||||
configuration "Release*"
|
filter "configurations:Release"
|
||||||
|
optimize "Size"
|
||||||
|
buildoptions {"/GL"}
|
||||||
|
linkoptions {"/IGNORE:4702", "/LTCG"}
|
||||||
defines {"NDEBUG"}
|
defines {"NDEBUG"}
|
||||||
flags { "MultiProcessorCompile", "LinkTimeOptimization", "No64BitChecks" }
|
flags {"FatalCompileWarnings", "FatalLinkWarnings"}
|
||||||
optimize "On"
|
|
||||||
|
|
||||||
if not _OPTIONS["force-unit-tests"] then
|
if not _OPTIONS["force-unit-tests"] then
|
||||||
rtti ("Off")
|
rtti ("Off")
|
||||||
end
|
end
|
||||||
|
filter {}
|
||||||
|
|
||||||
configuration "Debug*"
|
filter "configurations:Debug"
|
||||||
defines { "DEBUG", "_DEBUG" }
|
|
||||||
flags { "MultiProcessorCompile", "No64BitChecks" }
|
|
||||||
optimize "Debug"
|
optimize "Debug"
|
||||||
if symbols ~= nil then
|
defines {"DEBUG", "_DEBUG"}
|
||||||
symbols "On"
|
filter {}
|
||||||
else
|
|
||||||
flags { "Symbols" }
|
|
||||||
end
|
|
||||||
|
|
||||||
project "iw4x"
|
project "iw4x"
|
||||||
kind "SharedLib"
|
kind "SharedLib"
|
||||||
@ -291,7 +263,6 @@ workspace "iw4x"
|
|||||||
"./src/**.rc",
|
"./src/**.rc",
|
||||||
"./src/**.hpp",
|
"./src/**.hpp",
|
||||||
"./src/**.cpp",
|
"./src/**.cpp",
|
||||||
--"./src/**.proto",
|
|
||||||
}
|
}
|
||||||
includedirs {
|
includedirs {
|
||||||
"%{prj.location}/src",
|
"%{prj.location}/src",
|
||||||
@ -324,47 +295,15 @@ workspace "iw4x"
|
|||||||
if _OPTIONS["force-exception-handler"] then
|
if _OPTIONS["force-exception-handler"] then
|
||||||
defines {"FORCE_EXCEPTION_HANDLER"}
|
defines {"FORCE_EXCEPTION_HANDLER"}
|
||||||
end
|
end
|
||||||
|
if _OPTIONS["iw4x-zones"] then
|
||||||
|
defines {"GENERATE_IW4X_SPECIFIC_ZONES"}
|
||||||
|
end
|
||||||
|
|
||||||
-- Pre-compiled header
|
-- Pre-compiled header
|
||||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||||
pchsource "src/STDInclude.cpp" -- real path
|
pchsource "src/STDInclude.cpp" -- real path
|
||||||
buildoptions { "/Zm200" }
|
|
||||||
|
|
||||||
-- Dependency libraries
|
dependencies.imports()
|
||||||
json11.import()
|
|
||||||
libtomcrypt.import()
|
|
||||||
libtommath.import()
|
|
||||||
mongoose.import()
|
|
||||||
pdcurses.import()
|
|
||||||
protobuf.import()
|
|
||||||
zlib.import()
|
|
||||||
udis86.import()
|
|
||||||
--iw4mvm.import()
|
|
||||||
dxsdk.import()
|
|
||||||
|
|
||||||
-- fix vpaths for protobuf sources
|
|
||||||
vpaths
|
|
||||||
{
|
|
||||||
["*"] = { "./src/**" },
|
|
||||||
--["Proto/Generated"] = { "**.pb.*" }, -- meh.
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Virtual paths
|
|
||||||
if not _OPTIONS["no-new-structure"] then
|
|
||||||
vpaths
|
|
||||||
{
|
|
||||||
["Headers/*"] = { "./src/**.hpp" },
|
|
||||||
["Sources/*"] = { "./src/**.cpp" },
|
|
||||||
["Resource/*"] = { "./src/**.rc" },
|
|
||||||
--["Proto/Definitions/*"] = { "./src/Proto/**.proto" },
|
|
||||||
--["Proto/Generated/*"] = { "**.pb.*" }, -- meh.
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
vpaths
|
|
||||||
{
|
|
||||||
["Docs/*"] = { "**.txt","**.md" },
|
|
||||||
}
|
|
||||||
|
|
||||||
-- Pre-build
|
-- Pre-build
|
||||||
prebuildcommands
|
prebuildcommands
|
||||||
@ -393,74 +332,9 @@ workspace "iw4x"
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Specific configurations
|
|
||||||
flags { "UndefinedIdentifiers" }
|
|
||||||
warnings "Extra"
|
|
||||||
|
|
||||||
if symbols ~= nil then
|
group "External Dependencies"
|
||||||
symbols "On"
|
dependencies.projects()
|
||||||
else
|
|
||||||
flags { "Symbols" }
|
|
||||||
end
|
|
||||||
|
|
||||||
configuration "Release*"
|
|
||||||
flags {
|
|
||||||
"FatalCompileWarnings",
|
|
||||||
"FatalLinkWarnings",
|
|
||||||
}
|
|
||||||
configuration {}
|
|
||||||
|
|
||||||
--[[
|
|
||||||
-- Generate source code from protobuf definitions
|
|
||||||
rules { "ProtobufCompiler" }
|
|
||||||
|
|
||||||
-- Workaround: Consume protobuf generated source files
|
|
||||||
matches = os.matchfiles(path.join("src/Proto/**.proto"))
|
|
||||||
for i, srcPath in ipairs(matches) do
|
|
||||||
basename = path.getbasename(srcPath)
|
|
||||||
files
|
|
||||||
{
|
|
||||||
string.format("%%{prj.location}/src/proto/%s.pb.h", basename),
|
|
||||||
string.format("%%{prj.location}/src/proto/%s.pb.cc", basename),
|
|
||||||
}
|
|
||||||
end
|
|
||||||
includedirs
|
|
||||||
{
|
|
||||||
"%{prj.location}/src/proto",
|
|
||||||
}
|
|
||||||
filter "files:**.pb.*"
|
|
||||||
flags {
|
|
||||||
"NoPCH",
|
|
||||||
}
|
|
||||||
buildoptions {
|
|
||||||
"/wd4100", -- "Unused formal parameter"
|
|
||||||
"/wd4389", -- "Signed/Unsigned mismatch"
|
|
||||||
"/wd6011", -- "Dereferencing NULL pointer"
|
|
||||||
"/wd4125", -- "Decimal digit terminates octal escape sequence"
|
|
||||||
}
|
|
||||||
defines {
|
|
||||||
"_SCL_SECURE_NO_WARNINGS",
|
|
||||||
}
|
|
||||||
filter {}
|
|
||||||
]]
|
|
||||||
|
|
||||||
group "External dependencies"
|
|
||||||
json11.project()
|
|
||||||
libtomcrypt.project()
|
|
||||||
libtommath.project()
|
|
||||||
mongoose.project()
|
|
||||||
pdcurses.project()
|
|
||||||
protobuf.project()
|
|
||||||
zlib.project()
|
|
||||||
udis86.project()
|
|
||||||
--iw4mvm.project()
|
|
||||||
|
|
||||||
workspace "*"
|
|
||||||
buildoptions {
|
|
||||||
"/std:c++latest"
|
|
||||||
}
|
|
||||||
systemversion "latest"
|
|
||||||
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
|
|
||||||
|
|
||||||
rule "ProtobufCompiler"
|
rule "ProtobufCompiler"
|
||||||
display "Protobuf compiler"
|
display "Protobuf compiler"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -47,11 +47,7 @@ namespace Components
|
|||||||
Loader::Register(new Toast());
|
Loader::Register(new Toast());
|
||||||
Loader::Register(new Party());
|
Loader::Register(new Party());
|
||||||
Loader::Register(new Zones());
|
Loader::Register(new Zones());
|
||||||
Loader::Register(new Colors());
|
|
||||||
Loader::Register(new D3D9Ex());
|
Loader::Register(new D3D9Ex());
|
||||||
#if (!defined(VLD_RPTHOOK_INSTALL) || defined(VLDEnable)) && defined(COMPILE_IW4MVM) // IW4MVM uses detours which produces memory leaks, but those are not really relevant
|
|
||||||
Loader::Register(new IW4MVM());
|
|
||||||
#endif
|
|
||||||
Loader::Register(new Logger());
|
Loader::Register(new Logger());
|
||||||
Loader::Register(new Script());
|
Loader::Register(new Script());
|
||||||
Loader::Register(new Weapon());
|
Loader::Register(new Weapon());
|
||||||
@ -101,6 +97,13 @@ namespace Components
|
|||||||
Loader::Register(new StructuredData());
|
Loader::Register(new StructuredData());
|
||||||
Loader::Register(new ConnectProtocol());
|
Loader::Register(new ConnectProtocol());
|
||||||
Loader::Register(new StartupMessages());
|
Loader::Register(new StartupMessages());
|
||||||
|
Loader::Register(new SoundMutexFix());
|
||||||
|
Loader::Register(new Gamepad());
|
||||||
|
Loader::Register(new Chat());
|
||||||
|
Loader::Register(new TextRenderer());
|
||||||
|
Loader::Register(new Movement());
|
||||||
|
Loader::Register(new Elevators());
|
||||||
|
Loader::Register(new ClientCommand());
|
||||||
|
|
||||||
Loader::Register(new Client());
|
Loader::Register(new Client());
|
||||||
|
|
||||||
|
@ -73,7 +73,6 @@ namespace Components
|
|||||||
#include "Modules/Menus.hpp"
|
#include "Modules/Menus.hpp"
|
||||||
#include "Modules/Toast.hpp"
|
#include "Modules/Toast.hpp"
|
||||||
#include "Modules/Zones.hpp"
|
#include "Modules/Zones.hpp"
|
||||||
#include "Modules/Colors.hpp"
|
|
||||||
#include "Modules/D3D9Ex.hpp"
|
#include "Modules/D3D9Ex.hpp"
|
||||||
#include "Modules/Script.hpp"
|
#include "Modules/Script.hpp"
|
||||||
#include "Modules/Weapon.hpp"
|
#include "Modules/Weapon.hpp"
|
||||||
@ -89,7 +88,6 @@ namespace Components
|
|||||||
#include "Modules/Node.hpp"
|
#include "Modules/Node.hpp"
|
||||||
#include "Modules/RCon.hpp"
|
#include "Modules/RCon.hpp"
|
||||||
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
|
#include "Modules/Party.hpp" // Destroys the order, but requires network classes :D
|
||||||
#include "Modules/IW4MVM.hpp"
|
|
||||||
#include "Modules/Logger.hpp"
|
#include "Modules/Logger.hpp"
|
||||||
#include "Modules/Friends.hpp"
|
#include "Modules/Friends.hpp"
|
||||||
#include "Modules/IPCPipe.hpp"
|
#include "Modules/IPCPipe.hpp"
|
||||||
@ -130,5 +128,12 @@ namespace Components
|
|||||||
#include "Modules/ConnectProtocol.hpp"
|
#include "Modules/ConnectProtocol.hpp"
|
||||||
#include "Modules/StartupMessages.hpp"
|
#include "Modules/StartupMessages.hpp"
|
||||||
#include "Modules/Stats.hpp"
|
#include "Modules/Stats.hpp"
|
||||||
|
#include "Modules/SoundMutexFix.hpp"
|
||||||
|
#include "Modules/Chat.hpp"
|
||||||
|
#include "Modules/TextRenderer.hpp"
|
||||||
|
#include "Modules/Movement.hpp"
|
||||||
|
#include "Modules/Elevators.hpp"
|
||||||
|
#include "Modules/ClientCommand.hpp"
|
||||||
|
|
||||||
|
#include "Modules/Gamepad.hpp"
|
||||||
#include "Modules/Client.hpp"
|
#include "Modules/Client.hpp"
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -634,7 +634,7 @@ namespace Components
|
|||||||
LUID luid;
|
LUID luid;
|
||||||
TOKEN_PRIVILEGES tp = { 0 };
|
TOKEN_PRIVILEGES tp = { 0 };
|
||||||
DWORD cb = sizeof(TOKEN_PRIVILEGES);
|
DWORD cb = sizeof(TOKEN_PRIVILEGES);
|
||||||
if (!LookupPrivilegeValueW(nullptr, SE_DEBUG_NAME, &luid)) return;
|
if (!LookupPrivilegeValueA(nullptr, SE_DEBUG_NAME, &luid)) return;
|
||||||
|
|
||||||
tp.PrivilegeCount = 1;
|
tp.PrivilegeCount = 1;
|
||||||
tp.Privileges[0].Luid = luid;
|
tp.Privileges[0].Luid = luid;
|
||||||
@ -889,12 +889,8 @@ namespace Components
|
|||||||
time(nullptr);
|
time(nullptr);
|
||||||
AntiCheat::Flags = NO_FLAG;
|
AntiCheat::Flags = NO_FLAG;
|
||||||
|
|
||||||
#ifdef DISABLE_ANTICHEAT
|
#ifndef DISABLE_ANTICHEAT
|
||||||
Command::Add("penis", [](Command::Params*)
|
|
||||||
{
|
|
||||||
AntiCheat::CrashClient();
|
|
||||||
});
|
|
||||||
#else
|
|
||||||
Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick();
|
Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick();
|
||||||
Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick();
|
Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick();
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <StdInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -119,7 +119,6 @@ namespace Components
|
|||||||
push esi
|
push esi
|
||||||
push edi
|
push edi
|
||||||
|
|
||||||
|
|
||||||
push eax
|
push eax
|
||||||
pushad
|
pushad
|
||||||
|
|
||||||
@ -130,14 +129,12 @@ namespace Components
|
|||||||
popad
|
popad
|
||||||
pop eax
|
pop eax
|
||||||
|
|
||||||
|
|
||||||
test al, al
|
test al, al
|
||||||
jnz checkTempAssets
|
jnz checkTempAssets
|
||||||
|
|
||||||
mov ecx, [esp + 18h] // Asset type
|
mov ecx, [esp + 18h] // Asset type
|
||||||
mov ebx, [esp + 1Ch] // Filename
|
mov ebx, [esp + 1Ch] // Filename
|
||||||
|
|
||||||
|
|
||||||
push eax
|
push eax
|
||||||
pushad
|
pushad
|
||||||
|
|
||||||
@ -152,7 +149,6 @@ namespace Components
|
|||||||
popad
|
popad
|
||||||
pop eax
|
pop eax
|
||||||
|
|
||||||
|
|
||||||
test eax, eax
|
test eax, eax
|
||||||
jnz finishFound
|
jnz finishFound
|
||||||
|
|
||||||
@ -286,7 +282,7 @@ namespace Components
|
|||||||
push [esp + 2Ch]
|
push [esp + 2Ch]
|
||||||
push [esp + 2Ch]
|
push [esp + 2Ch]
|
||||||
call AssetHandler::IsAssetEligible
|
call AssetHandler::IsAssetEligible
|
||||||
add esp, 08h
|
add esp, 8h
|
||||||
|
|
||||||
mov [esp + 20h], eax
|
mov [esp + 20h], eax
|
||||||
popad
|
popad
|
||||||
@ -295,13 +291,13 @@ namespace Components
|
|||||||
test al, al
|
test al, al
|
||||||
jz doNotLoad
|
jz doNotLoad
|
||||||
|
|
||||||
mov eax, [esp + 8]
|
mov eax, [esp + 8h]
|
||||||
sub esp, 14h
|
sub esp, 14h
|
||||||
mov ecx, 5BB657h
|
mov ecx, 5BB657h
|
||||||
jmp ecx
|
jmp ecx
|
||||||
|
|
||||||
doNotLoad:
|
doNotLoad:
|
||||||
mov eax, [esp + 8]
|
mov eax, [esp + 8h]
|
||||||
retn
|
retn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,7 +494,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
this->reallocateEntryPool();
|
this->reallocateEntryPool();
|
||||||
|
|
||||||
Dvar::Register<bool>("r_noVoid", false, Game::DVAR_FLAG_SAVED, "Disable void model (red fx)");
|
Dvar::Register<bool>("r_noVoid", false, Game::DVAR_ARCHIVE, "Disable void model (red fx)");
|
||||||
|
|
||||||
AssetHandler::ClearTemporaryAssets();
|
AssetHandler::ClearTemporaryAssets();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_COMMAP_VERSION 0
|
#define IW4X_COMMAP_VERSION 0
|
||||||
|
|
||||||
|
@ -1,7 +1,82 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include <stb_truetype.h>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int PackFonts(const uint8_t* data, std::vector<uint16_t>& charset, Game::Glyph* glyphs, float pixel_height, unsigned char* pixels, int pw, int ph, int yOffset)
|
||||||
|
{
|
||||||
|
stbtt_fontinfo f;
|
||||||
|
f.userdata = NULL;
|
||||||
|
|
||||||
|
if (!stbtt_InitFont(&f, data, 0))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
std::memset(pixels, 0, pw * ph);
|
||||||
|
|
||||||
|
int x = 1, y = 1, bottom_y = 1;
|
||||||
|
|
||||||
|
float scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (auto& ch : charset)
|
||||||
|
{
|
||||||
|
int advance, lsb, x0, y0, x1, y1, gw, gh;
|
||||||
|
|
||||||
|
int g = stbtt_FindGlyphIndex(&f, ch);
|
||||||
|
|
||||||
|
stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
|
||||||
|
stbtt_GetGlyphBitmapBox(&f, g, scale, scale, &x0, &y0, &x1, &y1);
|
||||||
|
|
||||||
|
gw = x1 - x0;
|
||||||
|
gh = y1 - y0;
|
||||||
|
|
||||||
|
if (x + gw + 1 >= pw)
|
||||||
|
{
|
||||||
|
// Advance to next row
|
||||||
|
y = bottom_y;
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y + gh + 1 >= ph)
|
||||||
|
{
|
||||||
|
// Check if we have ran out of the room
|
||||||
|
return -i;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbtt_MakeGlyphBitmap(&f, pixels + x + y * pw, gw, gh, pw, scale, scale, g);
|
||||||
|
|
||||||
|
auto& glyph = glyphs[i++];
|
||||||
|
|
||||||
|
glyph.letter = ch;
|
||||||
|
glyph.s0 = x / static_cast<float>(pw);
|
||||||
|
glyph.s1 = (x + gw) / static_cast<float>(pw);
|
||||||
|
glyph.t0 = y / static_cast<float>(ph);
|
||||||
|
glyph.t1 = (y + gh) / static_cast<float>(ph);
|
||||||
|
glyph.pixelWidth = static_cast<char>(gw);
|
||||||
|
glyph.pixelHeight = static_cast<char>(gh);
|
||||||
|
glyph.x0 = static_cast<char>(x0);
|
||||||
|
glyph.y0 = static_cast<char>(y0 + yOffset);
|
||||||
|
glyph.dx = static_cast<char>(std::roundf(scale * advance));
|
||||||
|
|
||||||
|
// Advance to next col
|
||||||
|
x = x + gw + 1;
|
||||||
|
|
||||||
|
// Expand bottom of current row if current glyph is bigger
|
||||||
|
if (y + gh + 1 > bottom_y)
|
||||||
|
{
|
||||||
|
bottom_y = y + gh + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bottom_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IFont_s::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IFont_s::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Game::Font_s *asset = header.font;
|
Game::Font_s *asset = header.font;
|
||||||
@ -17,6 +92,164 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IFont_s::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
|
{
|
||||||
|
Components::FileSystem::File fontDefFile(Utils::String::VA("%s.json", name.data()));
|
||||||
|
Components::FileSystem::File fontFile(Utils::String::VA("%s.ttf", name.data()));
|
||||||
|
|
||||||
|
if (fontDefFile.exists() && fontFile.exists())
|
||||||
|
{
|
||||||
|
std::string errors;
|
||||||
|
auto fontDef = json11::Json::parse(fontDefFile.getBuffer(), errors);
|
||||||
|
|
||||||
|
if (!errors.empty())
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Font define %s is broken: %s.", name.data(), errors.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fontDef.is_object())
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Font define %s is invaild.", name.data(), errors.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = fontDef["textureWidth"].int_value();
|
||||||
|
int h = fontDef["textureHeight"].int_value();
|
||||||
|
|
||||||
|
int size = fontDef["size"].int_value();
|
||||||
|
int yOffset = fontDef["yOffset"].int_value();
|
||||||
|
|
||||||
|
auto* pixels = builder->getAllocator()->allocateArray<uint8_t>(w * h);
|
||||||
|
|
||||||
|
// Setup assets
|
||||||
|
const auto* texName = builder->getAllocator()->duplicateString(Utils::String::VA("if_%s", name.data() + 6 /* skip "fonts/" */));
|
||||||
|
const auto* fontName = builder->getAllocator()->duplicateString(name.data());
|
||||||
|
const auto* glowMaterialName = builder->getAllocator()->duplicateString(Utils::String::VA("%s_glow", name.data()));
|
||||||
|
|
||||||
|
auto* image = builder->getAllocator()->allocate<Game::GfxImage>();
|
||||||
|
std::memcpy(image, Game::DB_FindXAssetHeader(Game::ASSET_TYPE_IMAGE, "gamefonts_pc").image, sizeof(Game::GfxImage));
|
||||||
|
|
||||||
|
image->name = texName;
|
||||||
|
|
||||||
|
auto* material = builder->getAllocator()->allocate<Game::Material>();
|
||||||
|
std::memcpy(material, Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "fonts/gamefonts_pc").material, sizeof(Game::Material));
|
||||||
|
|
||||||
|
material->textureTable = builder->getAllocator()->allocate<Game::MaterialTextureDef>();
|
||||||
|
material->textureTable->u.image = image;
|
||||||
|
material->info.name = fontName;
|
||||||
|
|
||||||
|
auto* glowMaterial = builder->getAllocator()->allocate<Game::Material>();
|
||||||
|
std::memcpy(glowMaterial, Game::DB_FindXAssetHeader(Game::ASSET_TYPE_MATERIAL, "fonts/gamefonts_pc_glow").material, sizeof(Game::Material));
|
||||||
|
|
||||||
|
glowMaterial->textureTable = material->textureTable;
|
||||||
|
glowMaterial->info.name = glowMaterialName;
|
||||||
|
|
||||||
|
std::vector<uint16_t> charset;
|
||||||
|
|
||||||
|
if (fontDef["charset"].is_array())
|
||||||
|
{
|
||||||
|
for (auto& ch : fontDef["charset"].array_items())
|
||||||
|
charset.push_back(static_cast<uint16_t>(ch.int_value()));
|
||||||
|
|
||||||
|
// order matters
|
||||||
|
std::sort(charset.begin(), charset.end());
|
||||||
|
|
||||||
|
for (uint16_t i = 32; i < 128; i++)
|
||||||
|
{
|
||||||
|
if (std::find(charset.begin(), charset.end(), i) == charset.end())
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Font %s missing codepoint %d.", name.data(), i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint16_t i = 32; i < 128; i++)
|
||||||
|
charset.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto* font = builder->getAllocator()->allocate<Game::Font_s>();
|
||||||
|
|
||||||
|
font->fontName = fontName;
|
||||||
|
font->pixelHeight = size;
|
||||||
|
font->material = material;
|
||||||
|
font->glowMaterial = glowMaterial;
|
||||||
|
font->glyphCount = charset.size();
|
||||||
|
font->glyphs = builder->getAllocator()->allocateArray<Game::Glyph>(charset.size());
|
||||||
|
|
||||||
|
// Generate glyph data
|
||||||
|
int result = PackFonts(reinterpret_cast<const uint8_t*>(fontFile.getBuffer().data()), charset, font->glyphs, static_cast<float>(size), pixels, w, h, yOffset);
|
||||||
|
|
||||||
|
if (result == -1)
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Truetype font %s is broken.", name.data());
|
||||||
|
}
|
||||||
|
else if (result < 0)
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Texture size of font %s is not enough.", name.data());
|
||||||
|
}
|
||||||
|
else if(h - result > size)
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Warn: Texture of font %s have too much left over space: %d\n", name.data(), h - result);
|
||||||
|
}
|
||||||
|
|
||||||
|
header->font = font;
|
||||||
|
|
||||||
|
// Save generated materials
|
||||||
|
Game::XAssetHeader tmpHeader;
|
||||||
|
|
||||||
|
tmpHeader.image = image;
|
||||||
|
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_IMAGE, tmpHeader);
|
||||||
|
|
||||||
|
tmpHeader.material = material;
|
||||||
|
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_MATERIAL, tmpHeader);
|
||||||
|
|
||||||
|
tmpHeader.material = glowMaterial;
|
||||||
|
Components::AssetHandler::StoreTemporaryAsset(Game::ASSET_TYPE_MATERIAL, tmpHeader);
|
||||||
|
|
||||||
|
// Save generated image
|
||||||
|
Utils::IO::CreateDir("userraw\\images");
|
||||||
|
|
||||||
|
int fileSize = w * h * 4;
|
||||||
|
int iwiHeaderSize = static_cast<int>(sizeof(Game::GfxImageFileHeader));
|
||||||
|
|
||||||
|
Game::GfxImageFileHeader iwiHeader =
|
||||||
|
{
|
||||||
|
{ 'I', 'W', 'i' },
|
||||||
|
/* version */
|
||||||
|
8,
|
||||||
|
/* flags */
|
||||||
|
2,
|
||||||
|
/* format */
|
||||||
|
Game::IMG_FORMAT_BITMAP_RGBA,
|
||||||
|
0,
|
||||||
|
/* dimensions(x, y, z) */
|
||||||
|
{ static_cast<short>(w), static_cast<short>(h), 1 },
|
||||||
|
/* fileSizeForPicmip (mipSize in bytes + sizeof(GfxImageFileHeader)) */
|
||||||
|
{ fileSize + iwiHeaderSize, fileSize, fileSize, fileSize }
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string outIwi;
|
||||||
|
outIwi.resize(fileSize + sizeof(Game::GfxImageFileHeader));
|
||||||
|
|
||||||
|
std::memcpy(outIwi.data(), &iwiHeader, sizeof(Game::GfxImageFileHeader));
|
||||||
|
|
||||||
|
// Generate RGBA data
|
||||||
|
auto* rgbaPixels = outIwi.data() + sizeof(Game::GfxImageFileHeader);
|
||||||
|
|
||||||
|
for (int i = 0; i < w * h * 4; i += 4)
|
||||||
|
{
|
||||||
|
rgbaPixels[i + 0] = static_cast<char>(255);
|
||||||
|
rgbaPixels[i + 1] = static_cast<char>(255);
|
||||||
|
rgbaPixels[i + 2] = static_cast<char>(255);
|
||||||
|
rgbaPixels[i + 3] = static_cast<char>(pixels[i / 4]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils::IO::WriteFile(Utils::String::VA("userraw\\images\\%s.iwi", texName), outIwi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void IFont_s::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
AssertSize(Game::Font_s, 24);
|
AssertSize(Game::Font_s, 24);
|
||||||
|
@ -9,6 +9,6 @@ namespace Assets
|
|||||||
|
|
||||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||||
// virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override;
|
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_FX_VERSION 1
|
#define IW4X_FX_VERSION 1
|
||||||
|
|
||||||
@ -7,12 +7,13 @@ namespace Assets
|
|||||||
void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx
|
if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx
|
||||||
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
|
|
||||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||||
|
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
void IFxEffectDef::loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
||||||
{
|
{
|
||||||
|
|
||||||
switch (elemType)
|
switch (elemType)
|
||||||
{
|
{
|
||||||
case Game::FX_ELEM_TYPE_MODEL:
|
case Game::FX_ELEM_TYPE_MODEL:
|
||||||
@ -34,10 +35,7 @@ namespace Assets
|
|||||||
if (visuals->soundName)
|
if (visuals->soundName)
|
||||||
{
|
{
|
||||||
visuals->soundName = reader->readCString();
|
visuals->soundName = reader->readCString();
|
||||||
visuals->soundName = "null";
|
|
||||||
Components::Logger::Print("Unable to load sounds yet!\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "StdInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_IMG_VERSION "0"
|
#define IW4X_IMG_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_LIGHT_VERSION "0"
|
#define IW4X_LIGHT_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_GFXMAP_VERSION 1
|
#define IW4X_GFXMAP_VERSION 1
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File soundFile(Utils::String::VA("sounds/%s", name.data()));
|
Components::FileSystem::File soundFile(Utils::String::VA("loaded_sound/%s", name.data()));
|
||||||
if (!soundFile.exists())
|
if (!soundFile.exists())
|
||||||
{
|
{
|
||||||
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
|
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
|
||||||
@ -60,8 +60,8 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
sound->sound.info.channels = reader.read<short>();
|
sound->sound.info.channels = reader.read<short>();
|
||||||
sound->sound.info.samples = reader.read<int>();
|
|
||||||
sound->sound.info.rate = reader.read<int>();
|
sound->sound.info.rate = reader.read<int>();
|
||||||
|
sound->sound.info.samples = reader.read<int>();
|
||||||
sound->sound.info.block_size = reader.read<short>();
|
sound->sound.info.block_size = reader.read<short>();
|
||||||
sound->sound.info.bits = reader.read<short>();
|
sound->sound.info.bits = reader.read<short>();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_MAT_VERSION "1"
|
#define IW4X_MAT_VERSION "1"
|
||||||
|
|
||||||
@ -29,6 +29,33 @@ namespace Assets
|
|||||||
"_add_lin_nofog",
|
"_add_lin_nofog",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::map<std::string, std::string> techSetCorrespondance = {
|
||||||
|
{"effect", "effect_blend"},
|
||||||
|
{"effect", "effect_blend"},
|
||||||
|
{"effect_nofog", "effect_blend_nofog"},
|
||||||
|
{"effect_zfeather", "effect_zfeather_blend"},
|
||||||
|
|
||||||
|
{"wc_unlit_add", "wc_unlit_add_lin"},
|
||||||
|
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
|
||||||
|
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
|
||||||
|
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
|
||||||
|
{"wc_unlit", "wc_unlit_replace_lin"},
|
||||||
|
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
||||||
|
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
|
||||||
|
{"wc_unlit_replace", "wc_unlit_replace_lin"},
|
||||||
|
|
||||||
|
{"mc_unlit_replace", "mc_unlit_replace_lin"},
|
||||||
|
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
||||||
|
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
|
||||||
|
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
|
||||||
|
/*,
|
||||||
|
{"", ""},
|
||||||
|
{"", ""},
|
||||||
|
{"", ""},
|
||||||
|
{"", ""},
|
||||||
|
{"", ""},*/
|
||||||
|
};
|
||||||
|
|
||||||
Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data()));
|
Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data()));
|
||||||
if (!materialFile.exists()) return;
|
if (!materialFile.exists()) return;
|
||||||
|
|
||||||
@ -56,38 +83,41 @@ namespace Assets
|
|||||||
|
|
||||||
if (asset->techniqueSet)
|
if (asset->techniqueSet)
|
||||||
{
|
{
|
||||||
std::string techset = reader.readString();
|
std::string techsetName = reader.readString();
|
||||||
if (!techset.empty() && techset.front() == ',') techset.erase(techset.begin());
|
if (!techsetName.empty() && techsetName.front() == ',') techsetName.erase(techsetName.begin());
|
||||||
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techset.data(), builder).techniqueSet;
|
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
|
||||||
|
|
||||||
if (!asset->techniqueSet)
|
if (!asset->techniqueSet)
|
||||||
{
|
{
|
||||||
// Workaround for effect techsets having _nofog suffix
|
// Workaround for effect techsets having _nofog suffix
|
||||||
std::string suffix;
|
std::string suffix;
|
||||||
if (Utils::String::StartsWith(techset, "effect_") && Utils::String::EndsWith(techset, "_nofog"))
|
if (Utils::String::StartsWith(techsetName, "effect_") && Utils::String::EndsWith(techsetName, "_nofog"))
|
||||||
{
|
{
|
||||||
suffix = "_nofog";
|
suffix = "_nofog";
|
||||||
Utils::String::Replace(techset, suffix, "");
|
Utils::String::Replace(techsetName, suffix, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i)
|
for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i)
|
||||||
{
|
{
|
||||||
Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techset + techsetSuffix[i] + suffix).data(), builder).techniqueSet;
|
Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techsetName + techsetSuffix[i] + suffix).data(), builder).techniqueSet;
|
||||||
|
|
||||||
if (techsetPtr)
|
if (techsetPtr)
|
||||||
{
|
{
|
||||||
asset->techniqueSet = techsetPtr;
|
asset->techniqueSet = techsetPtr;
|
||||||
|
|
||||||
if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one
|
if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one
|
||||||
Components::Logger::Print("Techset '%s' has been mapped to '%s'\n", techset.data(), asset->techniqueSet->name);
|
Components::Logger::Print("Techset '%s' has been mapped to '%s'\n", techsetName.data(), asset->techniqueSet->name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Components::Logger::Print("Techset %s exists with the same name in iw4, and was mapped 1:1 with %s\n", techsetName.data(), asset->techniqueSet->name);
|
||||||
|
}
|
||||||
|
|
||||||
if (!asset->techniqueSet)
|
if (!asset->techniqueSet)
|
||||||
{
|
{
|
||||||
Components::Logger::Error("Missing techset: '%s' not found", techset.data());
|
Components::Logger::Error("Missing techset: '%s' not found", techsetName.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,11 +195,11 @@ namespace Assets
|
|||||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
||||||
asset->constantCount = header.material->constantCount;
|
asset->constantCount = header.material->constantCount;
|
||||||
asset->constantTable = header.material->constantTable;
|
asset->constantTable = header.material->constantTable;
|
||||||
|
Components::Logger::Print("For %s, copied constants & statebits from %s\n", asset->info.name, header.material->info.name);
|
||||||
replacementFound = true;
|
replacementFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, false, false);
|
}, false);
|
||||||
|
|
||||||
if (!replacementFound)
|
if (!replacementFound)
|
||||||
{
|
{
|
||||||
@ -185,12 +215,14 @@ namespace Assets
|
|||||||
if (!t1->techniques[i] && !t2->techniques[i]) continue;;
|
if (!t1->techniques[i] && !t2->techniques[i]) continue;;
|
||||||
if (!t1->techniques[i] || !t2->techniques[i]) return false;
|
if (!t1->techniques[i] || !t2->techniques[i]) return false;
|
||||||
|
|
||||||
if (t1->techniques[i]->flags != t1->techniques[i]->flags) return false;
|
// Apparently, this is really not that important
|
||||||
|
//if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
|
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
|
||||||
{
|
{
|
||||||
if (!replacementFound)
|
if (!replacementFound)
|
||||||
@ -204,13 +236,60 @@ namespace Assets
|
|||||||
replacementFound = true;
|
replacementFound = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, false, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!replacementFound && asset->techniqueSet)
|
if (!replacementFound && asset->techniqueSet)
|
||||||
{
|
{
|
||||||
|
|
||||||
Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name);
|
Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name);
|
||||||
|
std::string techName = asset->techniqueSet->name;
|
||||||
|
if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) {
|
||||||
|
auto iw4TechSetName = techSetCorrespondance[techName];
|
||||||
|
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
||||||
|
|
||||||
|
if (iw4TechSet)
|
||||||
|
{
|
||||||
|
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry)
|
||||||
|
{
|
||||||
|
if (!replacementFound)
|
||||||
|
{
|
||||||
|
Game::XAssetHeader header = entry->asset.header;
|
||||||
|
|
||||||
|
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet)
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Material %s with techset %s has been mapped to %s (last chance!), taking the sort key of material %s\n",
|
||||||
|
asset->info.name, asset->techniqueSet->name,
|
||||||
|
header.material->techniqueSet->name, header.material->info.name);
|
||||||
|
|
||||||
|
asset->info.sortKey = header.material->info.sortKey;
|
||||||
|
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
|
||||||
|
|
||||||
|
// this is terrible!
|
||||||
|
asset->stateBitsCount = header.material->stateBitsCount;
|
||||||
|
asset->stateBitsTable = header.material->stateBitsTable;
|
||||||
|
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
||||||
|
asset->constantCount = header.material->constantCount;
|
||||||
|
asset->constantTable = header.material->constantTable;
|
||||||
|
|
||||||
|
replacementFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
if (!replacementFound)
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Could not find any loaded material with techset %s (in replacement of %s), so I cannot set the sortkey for material %s\n", iw4TechSetName.data(), asset->techniqueSet->name, asset->info.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Could not find any loaded techset with iw4 name %s for iw3 techset %s\n", iw4TechSetName.data(), asset->techniqueSet->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::Print("Could not match iw3 techset %s with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName.data());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reader.end())
|
if (!reader.end())
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <StdInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_TECHSET_VERSION "0"
|
#define IW4X_TECHSET_VERSION "0"
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
@ -119,6 +119,79 @@ namespace Assets
|
|||||||
if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect);
|
if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect);
|
||||||
if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect);
|
if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect);
|
||||||
if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect);
|
if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect);
|
||||||
|
|
||||||
|
#define LoadWeapSound(sound) if (asset->weapDef->##sound##) builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND, asset->weapDef->##sound##)
|
||||||
|
|
||||||
|
LoadWeapSound(pickupSound);
|
||||||
|
LoadWeapSound(pickupSoundPlayer);
|
||||||
|
LoadWeapSound(ammoPickupSound);
|
||||||
|
LoadWeapSound(ammoPickupSoundPlayer);
|
||||||
|
LoadWeapSound(projectileSound);
|
||||||
|
LoadWeapSound(pullbackSound);
|
||||||
|
LoadWeapSound(pullbackSoundPlayer);
|
||||||
|
LoadWeapSound(fireSound);
|
||||||
|
LoadWeapSound(fireSoundPlayer);
|
||||||
|
LoadWeapSound(fireSoundPlayerAkimbo);
|
||||||
|
LoadWeapSound(fireLoopSound);
|
||||||
|
LoadWeapSound(fireLoopSoundPlayer);
|
||||||
|
LoadWeapSound(fireStopSound);
|
||||||
|
LoadWeapSound(fireStopSoundPlayer);
|
||||||
|
LoadWeapSound(fireLastSound);
|
||||||
|
LoadWeapSound(fireLastSoundPlayer);
|
||||||
|
LoadWeapSound(emptyFireSound);
|
||||||
|
LoadWeapSound(emptyFireSoundPlayer);
|
||||||
|
LoadWeapSound(meleeSwipeSound);
|
||||||
|
LoadWeapSound(meleeSwipeSoundPlayer);
|
||||||
|
LoadWeapSound(meleeHitSound);
|
||||||
|
LoadWeapSound(meleeMissSound);
|
||||||
|
LoadWeapSound(rechamberSound);
|
||||||
|
LoadWeapSound(rechamberSoundPlayer);
|
||||||
|
LoadWeapSound(reloadSound);
|
||||||
|
LoadWeapSound(reloadSoundPlayer);
|
||||||
|
LoadWeapSound(reloadEmptySound);
|
||||||
|
LoadWeapSound(reloadEmptySoundPlayer);
|
||||||
|
LoadWeapSound(reloadStartSound);
|
||||||
|
LoadWeapSound(reloadStartSoundPlayer);
|
||||||
|
LoadWeapSound(reloadEndSound);
|
||||||
|
LoadWeapSound(reloadEndSoundPlayer);
|
||||||
|
LoadWeapSound(detonateSound);
|
||||||
|
LoadWeapSound(detonateSoundPlayer);
|
||||||
|
LoadWeapSound(nightVisionWearSound);
|
||||||
|
LoadWeapSound(nightVisionWearSoundPlayer);
|
||||||
|
LoadWeapSound(nightVisionRemoveSound);
|
||||||
|
LoadWeapSound(nightVisionRemoveSoundPlayer);
|
||||||
|
LoadWeapSound(altSwitchSound);
|
||||||
|
LoadWeapSound(altSwitchSoundPlayer);
|
||||||
|
LoadWeapSound(raiseSound);
|
||||||
|
LoadWeapSound(raiseSoundPlayer);
|
||||||
|
LoadWeapSound(firstRaiseSound);
|
||||||
|
LoadWeapSound(firstRaiseSoundPlayer);
|
||||||
|
LoadWeapSound(putawaySound);
|
||||||
|
LoadWeapSound(putawaySoundPlayer);
|
||||||
|
LoadWeapSound(scanSound);
|
||||||
|
|
||||||
|
if (asset->weapDef->bounceSound)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < 31; i++)
|
||||||
|
{
|
||||||
|
LoadWeapSound(bounceSound[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadWeapSound(projExplosionSound);
|
||||||
|
LoadWeapSound(projDudSound);
|
||||||
|
LoadWeapSound(projIgnitionSound);
|
||||||
|
LoadWeapSound(turretOverheatSound);
|
||||||
|
LoadWeapSound(turretBarrelSpinMaxSnd);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
LoadWeapSound(turretBarrelSpinUpSnd[i]);
|
||||||
|
LoadWeapSound(turretBarrelSpinDownSnd[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoadWeapSound(missileConeSoundAlias);
|
||||||
|
LoadWeapSound(missileConeSoundAliasAtBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer)
|
void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer)
|
||||||
@ -274,9 +347,9 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
buffer->align(Utils::Stream::ALIGN_4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
int* ptrs = buffer->dest<int>();
|
int* ptrs = buffer->dest<int>();
|
||||||
buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*));
|
buffer->saveMax(31 * sizeof(Game::snd_alias_list_t*));
|
||||||
|
|
||||||
for (int i = 0; i < 37; i++)
|
for (int i = 0; i < 31; i++)
|
||||||
{
|
{
|
||||||
if (!def->bounceSound[i])
|
if (!def->bounceSound[i])
|
||||||
{
|
{
|
||||||
@ -433,14 +506,16 @@ namespace Assets
|
|||||||
|
|
||||||
if (def->projExplosionSound)
|
if (def->projExplosionSound)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->projExplosionSound->aliasName);
|
buffer->saveString(def->projExplosionSound->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->projExplosionSound);
|
Utils::Stream::ClearPointer(&dest->projExplosionSound);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->projDudSound)
|
if (def->projDudSound)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->projDudSound->aliasName);
|
buffer->saveString(def->projDudSound->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->projDudSound);
|
Utils::Stream::ClearPointer(&dest->projDudSound);
|
||||||
}
|
}
|
||||||
@ -476,7 +551,8 @@ namespace Assets
|
|||||||
|
|
||||||
if (def->projIgnitionSound)
|
if (def->projIgnitionSound)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->projIgnitionSound->aliasName);
|
buffer->saveString(def->projIgnitionSound->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->projIgnitionSound);
|
Utils::Stream::ClearPointer(&dest->projIgnitionSound);
|
||||||
}
|
}
|
||||||
@ -551,7 +627,8 @@ namespace Assets
|
|||||||
|
|
||||||
if (def->turretOverheatSound)
|
if (def->turretOverheatSound)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->turretOverheatSound->aliasName);
|
buffer->saveString(def->turretOverheatSound->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->turretOverheatSound);
|
Utils::Stream::ClearPointer(&dest->turretOverheatSound);
|
||||||
}
|
}
|
||||||
@ -569,7 +646,8 @@ namespace Assets
|
|||||||
|
|
||||||
if (def->turretBarrelSpinMaxSnd)
|
if (def->turretBarrelSpinMaxSnd)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName);
|
buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd);
|
Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd);
|
||||||
}
|
}
|
||||||
@ -577,7 +655,8 @@ namespace Assets
|
|||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (!def->turretBarrelSpinUpSnd[i]) continue;
|
if (!def->turretBarrelSpinUpSnd[i]) continue;
|
||||||
|
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName);
|
buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]);
|
Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]);
|
||||||
}
|
}
|
||||||
@ -585,21 +664,24 @@ namespace Assets
|
|||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if (!def->turretBarrelSpinDownSnd[i]) continue;
|
if (!def->turretBarrelSpinDownSnd[i]) continue;
|
||||||
|
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName);
|
buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]);
|
Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->missileConeSoundAlias)
|
if (def->missileConeSoundAlias)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->missileConeSoundAlias->aliasName);
|
buffer->saveString(def->missileConeSoundAlias->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->missileConeSoundAlias);
|
Utils::Stream::ClearPointer(&dest->missileConeSoundAlias);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->missileConeSoundAliasAtBase)
|
if (def->missileConeSoundAliasAtBase)
|
||||||
{
|
{
|
||||||
buffer->saveMax(4);
|
buffer->align(Utils::Stream::ALIGN_4);
|
||||||
|
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||||
buffer->saveString(def->missileConeSoundAliasAtBase->aliasName);
|
buffer->saveString(def->missileConeSoundAliasAtBase->aliasName);
|
||||||
Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase);
|
Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_ANIM_VERSION 1
|
#define IW4X_ANIM_VERSION 1
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_MODEL_VERSION 5
|
#define IW4X_MODEL_VERSION 5
|
||||||
|
|
||||||
@ -84,13 +84,14 @@ namespace Assets
|
|||||||
|
|
||||||
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
if (!builder->isPrimaryAsset())
|
Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data()));
|
||||||
|
|
||||||
|
if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::PreferDiskAssetsDvar.get<bool>() || !modelFile.exists()))
|
||||||
{
|
{
|
||||||
header->model = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).model;
|
header->model = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).model;
|
||||||
if (header->model) return;
|
if (header->model) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data()));
|
|
||||||
|
|
||||||
if (modelFile.exists())
|
if (modelFile.exists())
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#include "StdInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define IW4X_CLIPMAP_VERSION 1
|
#define IW4X_CLIPMAP_VERSION 2
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
@ -605,7 +605,7 @@ namespace Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
int version = reader.read<int>();
|
int version = reader.read<int>();
|
||||||
if (version != IW4X_CLIPMAP_VERSION)
|
if (version > IW4X_CLIPMAP_VERSION)
|
||||||
{
|
{
|
||||||
Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version);
|
Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version);
|
||||||
}
|
}
|
||||||
@ -881,33 +881,38 @@ namespace Assets
|
|||||||
clipMap->smodelNodeCount = reader.read<unsigned short>();
|
clipMap->smodelNodeCount = reader.read<unsigned short>();
|
||||||
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
||||||
|
|
||||||
clipMap->checksum = reader.read<int>();
|
|
||||||
|
|
||||||
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts;
|
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.data()), builder).mapEnts;
|
||||||
|
|
||||||
// add triggers to mapEnts
|
// add triggers to mapEnts
|
||||||
/*
|
if (version >= 2) {
|
||||||
std::list<Game::TriggerSlab> slabs;
|
if (clipMap->numSubModels > 0) {
|
||||||
std::list<Game::TriggerHull> hulls;
|
clipMap->mapEnts->trigger.count = clipMap->numSubModels;
|
||||||
std::list<Game::TriggerModel> models;
|
clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels;
|
||||||
|
|
||||||
for (int i = 0; i < clipMap->numCModels; ++i)
|
Game::TriggerHull* hulls = builder->getAllocator()->allocateArray<Game::TriggerHull>(clipMap->mapEnts->trigger.hullCount);
|
||||||
{
|
Game::TriggerModel* models = builder->getAllocator()->allocateArray<Game::TriggerModel>(clipMap->mapEnts->trigger.count);
|
||||||
Game::cLeafBrushNode_t* node = &clipMap->cLeafBrushNodes[clipMap->cModels[i].leaf.leafBrushNode];
|
|
||||||
if (!node->leafBrushCount) continue; // skip empty brushes
|
|
||||||
|
|
||||||
int baseHull = hulls.size();
|
for (unsigned int i = 0; i < clipMap->numSubModels; ++i)
|
||||||
for (int j = 0; j < node->leafBrushCount; ++j)
|
|
||||||
{
|
{
|
||||||
Game::cbrush_t* brush = &clipMap->cBrushes[node->data.leaf.brushes[j]];
|
models[i] = reader.read<Game::TriggerModel>();
|
||||||
int baseSlab = slabs.size();
|
hulls[i] = reader.read<Game::TriggerHull>();
|
||||||
for (int k = 0; k < brush->numsides; ++k)
|
}
|
||||||
{
|
|
||||||
Game::TriggerSlab curSlab;
|
size_t slabCount = reader.read<size_t>();
|
||||||
|
clipMap->mapEnts->trigger.slabCount = slabCount;
|
||||||
|
Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray<Game::TriggerSlab>(clipMap->mapEnts->trigger.slabCount);
|
||||||
|
for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) {
|
||||||
|
slabs[i] = reader.read<Game::TriggerSlab>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
clipMap->mapEnts->trigger.models = &models[0];
|
||||||
|
clipMap->mapEnts->trigger.hulls = &hulls[0];
|
||||||
|
clipMap->mapEnts->trigger.slabs = &slabs[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*/
|
clipMap->checksum = reader.read<int>();
|
||||||
|
|
||||||
// This mustn't be null and has to have at least 1 'valid' entry.
|
// This mustn't be null and has to have at least 1 'valid' entry.
|
||||||
if (!clipMap->smodelNodeCount || !clipMap->smodelNodes)
|
if (!clipMap->smodelNodeCount || !clipMap->smodelNodes)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <StdInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Assets
|
namespace Assets
|
||||||
{
|
{
|
||||||
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.data()));
|
Components::FileSystem::File aliasFile(Utils::String::VA("sounds/%s", name.c_str()));
|
||||||
if (!aliasFile.exists())
|
if (!aliasFile.exists())
|
||||||
{
|
{
|
||||||
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
|
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.c_str()).sound;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,12 +18,34 @@ namespace Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aliasList->head = builder->getAllocator()->allocate<Game::snd_alias_t>();
|
std::string errors;
|
||||||
|
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
|
||||||
|
json11::Json aliasesContainer = infoData["head"];
|
||||||
|
|
||||||
|
auto aliases = aliasesContainer.array_items();
|
||||||
|
|
||||||
|
aliasList->count = aliases.size();
|
||||||
|
|
||||||
|
// Allocate
|
||||||
|
aliasList->head = builder->getAllocator()->allocateArray<Game::snd_alias_t>(aliasList->count);
|
||||||
if (!aliasList->head)
|
if (!aliasList->head)
|
||||||
{
|
{
|
||||||
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
|
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
aliasList->aliasName = builder->getAllocator()->duplicateString(name.c_str());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < aliasList->count; i++)
|
||||||
|
{
|
||||||
|
json11::Json head = aliasesContainer[i];
|
||||||
|
|
||||||
|
if (!infoData.is_object())
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Failed to load sound %s!", name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
|
aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
|
||||||
if (!aliasList->head->soundFile)
|
if (!aliasList->head->soundFile)
|
||||||
{
|
{
|
||||||
@ -31,74 +53,176 @@ namespace Assets
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
aliasList->count = 1;
|
|
||||||
|
|
||||||
std::string errors;
|
|
||||||
json11::Json infoData = json11::Json::parse(aliasFile.getBuffer(), errors);
|
|
||||||
|
|
||||||
if (!infoData.is_object())
|
|
||||||
{
|
|
||||||
Components::Logger::Error("Failed to load sound %s!", name.data());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::snd_alias_t* alias = aliasList->head;
|
Game::snd_alias_t* alias = aliasList->head;
|
||||||
|
|
||||||
// try and parse everything and if it fails then fail for the whole file
|
// try and parse everything and if it fails then fail for the whole file
|
||||||
auto type = infoData["type"];
|
auto type = head["type"];
|
||||||
auto subtitle = infoData["subtitle"];
|
auto subtitle = head["subtitle"];
|
||||||
auto secondaryAliasName = infoData["secondaryAliasName"];
|
auto secondaryAliasName = head["secondaryAliasName"];
|
||||||
auto chainAliasName = infoData["chainAliasName"];
|
auto chainAliasName = head["chainAliasName"];
|
||||||
auto soundFile = infoData["soundFile"];
|
auto soundFile = head["soundFile"];
|
||||||
auto sequence = infoData["sequence"];
|
auto sequence = head["sequence"];
|
||||||
auto volMin = infoData["volMin"];
|
auto volMin = head["volMin"];
|
||||||
auto volMax = infoData["volMax"];
|
auto volMax = head["volMax"];
|
||||||
auto pitchMin = infoData["pitchMin"];
|
auto pitchMin = head["pitchMin"];
|
||||||
auto pitchMax = infoData["pitchMax"];
|
auto pitchMax = head["pitchMax"];
|
||||||
auto distMin = infoData["distMin"];
|
auto distMin = head["distMin"];
|
||||||
auto distMax = infoData["distMax"];
|
auto distMax = head["distMax"];
|
||||||
auto flags = infoData["flags"];
|
auto flags = head["flags"];
|
||||||
auto slavePercentage = infoData["slavePercentage"];
|
auto slavePercentage = head["slavePercentage"];
|
||||||
auto probability = infoData["probability"];
|
auto probability = head["probability"];
|
||||||
auto lfePercentage = infoData["lfePercentage"];
|
auto lfePercentage = head["lfePercentage"];
|
||||||
auto centerPercentage = infoData["centerPercentage"];
|
auto centerPercentage = head["centerPercentage"];
|
||||||
auto startDelay = infoData["startDelay"];
|
auto startDelay = head["startDelay"];
|
||||||
auto volumeFalloffCurve = infoData["volumeFalloffCurve"];
|
auto volumeFalloffCurve = head["volumeFalloffCurve"];
|
||||||
auto envelopMin = infoData["envelopMin"];
|
auto envelopMin = head["envelopMin"];
|
||||||
auto envelopMax = infoData["envelopMax"];
|
auto envelopMax = head["envelopMax"];
|
||||||
auto envelopPercentage = infoData["envelopPercentage"];
|
auto envelopPercentage = head["envelopPercentage"];
|
||||||
auto speakerMap = infoData["speakerMap"];
|
auto speakerMap = head["speakerMap"];
|
||||||
|
auto aliasName = head["aliasName"];
|
||||||
|
|
||||||
|
// Fix casing
|
||||||
|
if (soundFile.is_null())
|
||||||
|
{
|
||||||
|
soundFile = head["soundfile"];
|
||||||
|
|
||||||
|
Components::Logger::Print("Fixed casing on %s\n", name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
if (type.is_null() || soundFile.is_null())
|
if (type.is_null() || soundFile.is_null())
|
||||||
{
|
{
|
||||||
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile", name.data());
|
Components::Logger::Print("Type is %s\n", type.dump().c_str());
|
||||||
|
Components::Logger::Print("SoundFile is %s\n", soundFile.dump().c_str());
|
||||||
|
Components::Logger::Error("Failed to parse sound %s! Each alias must have at least a type and a soundFile\n", name.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECK(x, type) (x.is_##type##() || x.is_null())
|
#define CHECK(x, type) (x.is_##type##() || x.is_null())
|
||||||
|
|
||||||
// TODO: actually support all of those properties
|
// TODO: actually support all of those properties
|
||||||
if (CHECK(type, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
|
if (!CHECK(type, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "type", type.type(), type.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(subtitle, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "subtitle", subtitle.type(), subtitle.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(aliasName, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "aliasName", aliasName.type(), aliasName.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(secondaryAliasName, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "secondaryAliasName", secondaryAliasName.type(), secondaryAliasName.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(chainAliasName, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "chainAliasName", chainAliasName.type(), chainAliasName.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(soundFile, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "soundFile", soundFile.type(), soundFile.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(sequence, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "sequence", sequence.type(), sequence.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(volMin, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "volMin", volMin.type(), volMin.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(volMax, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "volMax", volMax.type(), volMax.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(pitchMin, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMin", pitchMin.type(), pitchMin.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(pitchMax, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "pitchMax", pitchMax.type(), pitchMax.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(probability, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "probability", probability.type(), probability.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(lfePercentage, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "lfePercentage", lfePercentage.type(), lfePercentage.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(centerPercentage, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "centerPercentage", centerPercentage.type(), centerPercentage.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(startDelay, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "startDelay", startDelay.type(), startDelay.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(volumeFalloffCurve, string))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not string but %d (%s)\n", "volumeFalloffCurve", volumeFalloffCurve.type(), volumeFalloffCurve.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(envelopMin, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMin", envelopMin.type(), envelopMin.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(envelopMax, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopMax", envelopMax.type(), envelopMax.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(envelopPercentage, number))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not number but %d (%s)\n", "envelopPercentage", envelopPercentage.type(), envelopPercentage.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CHECK(speakerMap, object))
|
||||||
|
{
|
||||||
|
Components::Logger::Print("%s is not object but %d (%s)\n", "speakerMap", speakerMap.type(), speakerMap.dump().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
|
||||||
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
|
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
|
||||||
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
|
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
|
||||||
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
|
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
|
||||||
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
|
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
|
||||||
CHECK(speakerMap, string))
|
CHECK(speakerMap, object))
|
||||||
{
|
{
|
||||||
|
|
||||||
alias->soundFile->exists = true;
|
alias->soundFile->exists = true;
|
||||||
|
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.string_value().c_str());
|
||||||
|
|
||||||
if (subtitle.is_string())
|
if (subtitle.is_string())
|
||||||
{
|
{
|
||||||
alias->subtitle = subtitle.string_value().data();
|
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.string_value().c_str());
|
||||||
}
|
}
|
||||||
if (secondaryAliasName.is_string())
|
if (secondaryAliasName.is_string())
|
||||||
{
|
{
|
||||||
alias->secondaryAliasName = secondaryAliasName.string_value().data();
|
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.string_value().c_str());
|
||||||
}
|
}
|
||||||
if (chainAliasName.is_string())
|
if (chainAliasName.is_string())
|
||||||
{
|
{
|
||||||
alias->chainAliasName = chainAliasName.string_value().data();
|
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.string_value().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
alias->sequence = sequence.int_value();
|
alias->sequence = sequence.int_value();
|
||||||
@ -118,35 +242,107 @@ namespace Assets
|
|||||||
alias->envelopMax = float(envelopMax.number_value());
|
alias->envelopMax = float(envelopMax.number_value());
|
||||||
alias->envelopPercentage = float(envelopPercentage.number_value());
|
alias->envelopPercentage = float(envelopPercentage.number_value());
|
||||||
|
|
||||||
if (volumeFalloffCurve.is_string())
|
// Speaker map object
|
||||||
|
if (!speakerMap.is_null())
|
||||||
{
|
{
|
||||||
alias->volumeFalloffCurve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, volumeFalloffCurve.string_value(), builder).sndCurve;
|
alias->speakerMap = builder->getAllocator()->allocate<Game::SpeakerMap>();
|
||||||
}
|
if (!alias->speakerMap)
|
||||||
|
|
||||||
if (type.string_value() == "loaded"s)
|
|
||||||
{
|
{
|
||||||
alias->soundFile->type = Game::SAT_LOADED;
|
Components::Logger::Print("Error allocating memory for speakermap in sound alias%s!\n", alias->aliasName);
|
||||||
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value(), builder).loadSnd;
|
|
||||||
}
|
|
||||||
else if (type.string_value() == "streamed"s)
|
|
||||||
{
|
|
||||||
alias->soundFile->type = Game::SAT_STREAMED;
|
|
||||||
std::string streamedFile = soundFile.string_value();
|
|
||||||
int split = streamedFile.find_last_of('/');
|
|
||||||
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(streamedFile.substr(0, split).c_str());
|
|
||||||
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.substr(split).c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s", name.data(), type.string_value().c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Components::Logger::Error("Failed to parse sound %s!", name.data());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].string_value().c_str());
|
||||||
|
alias->speakerMap->isDefault = speakerMap["isDefault"].bool_value();
|
||||||
|
|
||||||
|
if (speakerMap["channelMaps"].is_array())
|
||||||
|
{
|
||||||
|
json11::Json::array channelMaps = speakerMap["channelMaps"].array_items();
|
||||||
|
|
||||||
|
assert(channelMaps.size() <= 4);
|
||||||
|
|
||||||
|
// channelMapIndex should never exceed 1
|
||||||
|
for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++)
|
||||||
|
{
|
||||||
|
// subChannelIndex should never exceed 1
|
||||||
|
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
|
||||||
|
{
|
||||||
|
json11::Json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
|
||||||
|
|
||||||
|
auto speakers = channelMap["speakers"].array_items();
|
||||||
|
|
||||||
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
|
||||||
|
|
||||||
|
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
|
||||||
|
{
|
||||||
|
auto speaker = speakers[speakerIndex];
|
||||||
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = static_cast<float>(speaker["levels0"].number_value());
|
||||||
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = static_cast<float>(speaker["levels1"].number_value());
|
||||||
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = static_cast<int>(speaker["numLevels"].number_value());
|
||||||
|
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = static_cast<int>(speaker["speaker"].number_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (volumeFalloffCurve.is_string())
|
||||||
|
{
|
||||||
|
std::string fallOffCurve = volumeFalloffCurve.string_value();
|
||||||
|
|
||||||
|
if (fallOffCurve.size() == 0)
|
||||||
|
{
|
||||||
|
fallOffCurve = "$default";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto curve = Components::AssetHandler::FindAssetForZone(
|
||||||
|
Game::XAssetType::ASSET_TYPE_SOUND_CURVE,
|
||||||
|
fallOffCurve.c_str(),
|
||||||
|
builder
|
||||||
|
).sndCurve;
|
||||||
|
|
||||||
|
alias->volumeFalloffCurve = curve;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
|
||||||
|
{
|
||||||
|
alias->soundFile->type = Game::SAT_LOADED;
|
||||||
|
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.string_value().c_str(), builder).loadSnd;
|
||||||
|
}
|
||||||
|
else if (static_cast<Game::snd_alias_type_t>(type.number_value()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
|
||||||
|
{
|
||||||
|
alias->soundFile->type = Game::SAT_STREAMED;
|
||||||
|
|
||||||
|
std::string streamedFile = soundFile.string_value();
|
||||||
|
std::string directory = ""s;
|
||||||
|
int split = streamedFile.find_last_of('/');
|
||||||
|
|
||||||
|
if (split >= 0)
|
||||||
|
{
|
||||||
|
directory = streamedFile.substr(0, split);
|
||||||
|
streamedFile = streamedFile.substr(split+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str());
|
||||||
|
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Failed to parse sound %s! Invalid sound type %s\n", name.c_str(), type.string_value().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aliasList->head[i] = *alias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Components::Logger::Error("Failed to parse sound %s!\n", name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header->sound = aliasList;
|
||||||
|
|
||||||
#undef CHECK
|
#undef CHECK
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -155,7 +351,7 @@ namespace Assets
|
|||||||
{
|
{
|
||||||
Game::snd_alias_list_t* asset = header.sound;
|
Game::snd_alias_list_t* asset = header.sound;
|
||||||
|
|
||||||
for (int i = 0; i < asset->count; ++i)
|
for (unsigned int i = 0; i < asset->count; ++i)
|
||||||
{
|
{
|
||||||
Game::snd_alias_t* alias = &asset->head[i];
|
Game::snd_alias_t* alias = &asset->head[i];
|
||||||
|
|
||||||
@ -166,10 +362,15 @@ namespace Assets
|
|||||||
|
|
||||||
if (alias->volumeFalloffCurve)
|
if (alias->volumeFalloffCurve)
|
||||||
{
|
{
|
||||||
|
if (!builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve))
|
||||||
|
{
|
||||||
|
// (Should never happen, but just in case)
|
||||||
|
alias->volumeFalloffCurve->filename = "$default";
|
||||||
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve);
|
builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||||
{
|
{
|
||||||
@ -204,7 +405,7 @@ namespace Assets
|
|||||||
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
|
Game::snd_alias_t* destHead = buffer->dest<Game::snd_alias_t>();
|
||||||
buffer->saveArray(asset->head, asset->count);
|
buffer->saveArray(asset->head, asset->count);
|
||||||
|
|
||||||
for (int i = 0; i < asset->count; ++i)
|
for (unsigned int i = 0; i < asset->count; ++i)
|
||||||
{
|
{
|
||||||
Game::snd_alias_t* destAlias = &destHead[i];
|
Game::snd_alias_t* destAlias = &destHead[i];
|
||||||
Game::snd_alias_t* alias = &asset->head[i];
|
Game::snd_alias_t* alias = &asset->head[i];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -8,7 +8,8 @@ namespace Components
|
|||||||
Utils::Cryptography::Token Auth::ComputeToken;
|
Utils::Cryptography::Token Auth::ComputeToken;
|
||||||
Utils::Cryptography::ECC::Key Auth::GuidKey;
|
Utils::Cryptography::ECC::Key Auth::GuidKey;
|
||||||
|
|
||||||
std::vector<std::uint64_t> Auth::BannedUids = {
|
std::vector<std::uint64_t> Auth::BannedUids =
|
||||||
|
{
|
||||||
0xf4d2c30b712ac6e3,
|
0xf4d2c30b712ac6e3,
|
||||||
0xf7e33c4081337fa3,
|
0xf7e33c4081337fa3,
|
||||||
0x6f5597f103cc50e9
|
0x6f5597f103cc50e9
|
||||||
@ -82,7 +83,7 @@ namespace Components
|
|||||||
|
|
||||||
Command::ServerParams params;
|
Command::ServerParams params;
|
||||||
|
|
||||||
if (params.length() < 3)
|
if (params.size() < 3)
|
||||||
{
|
{
|
||||||
Game::SV_Cmd_EndTokenizedString();
|
Game::SV_Cmd_EndTokenizedString();
|
||||||
Logger::SoftError("Connecting failed: Command parsing error!");
|
Logger::SoftError("Connecting failed: Command parsing error!");
|
||||||
@ -169,7 +170,7 @@ namespace Components
|
|||||||
Command::ServerParams params;
|
Command::ServerParams params;
|
||||||
|
|
||||||
// Ensure there are enough params
|
// Ensure there are enough params
|
||||||
if (params.length() < 3)
|
if (params.size() < 3)
|
||||||
{
|
{
|
||||||
Network::Send(address, "error\nInvalid connect string!");
|
Network::Send(address, "error\nInvalid connect string!");
|
||||||
return;
|
return;
|
||||||
@ -179,8 +180,8 @@ namespace Components
|
|||||||
Utils::InfoString infostr(params[2]);
|
Utils::InfoString infostr(params[2]);
|
||||||
|
|
||||||
// Read the required data
|
// Read the required data
|
||||||
std::string steamId = infostr.get("xuid");
|
const auto& steamId = infostr.get("xuid");
|
||||||
std::string challenge = infostr.get("challenge");
|
const auto& challenge = infostr.get("challenge");
|
||||||
|
|
||||||
if (steamId.empty() || challenge.empty())
|
if (steamId.empty() || challenge.empty())
|
||||||
{
|
{
|
||||||
@ -189,7 +190,7 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the id
|
// Parse the id
|
||||||
unsigned __int64 xuid = strtoull(steamId.data(), nullptr, 16);
|
const auto xuid = std::strtoull(steamId.data(), nullptr, 16);
|
||||||
|
|
||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = xuid;
|
guid.bits = xuid;
|
||||||
@ -223,8 +224,8 @@ namespace Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Verify the security level
|
// Verify the security level
|
||||||
uint32_t ourLevel = static_cast<uint32_t>(Dvar::Var("sv_securityLevel").get<int>());
|
auto ourLevel = Dvar::Var("sv_securityLevel").get<unsigned int>();
|
||||||
uint32_t userLevel = Auth::GetZeroBits(connectData.token(), connectData.publickey());
|
auto userLevel = Auth::GetZeroBits(connectData.token(), connectData.publickey());
|
||||||
|
|
||||||
if (userLevel < ourLevel)
|
if (userLevel < ourLevel)
|
||||||
{
|
{
|
||||||
@ -431,7 +432,7 @@ namespace Components
|
|||||||
Scheduler::OnFrame(Auth::Frame);
|
Scheduler::OnFrame(Auth::Frame);
|
||||||
|
|
||||||
// Register dvar
|
// Register dvar
|
||||||
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_FLAG_SERVERINFO, "Security level for GUID certificates (POW)");
|
Dvar::Register<int>("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)");
|
||||||
|
|
||||||
// Install registration hook
|
// Install registration hook
|
||||||
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();
|
Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick();
|
||||||
@ -454,7 +455,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Command::Add("securityLevel", [](Command::Params* params)
|
Command::Add("securityLevel", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->length() < 2)
|
if (params->size() < 2)
|
||||||
{
|
{
|
||||||
uint32_t level = Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey());
|
uint32_t level = Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey());
|
||||||
Logger::Print("Your current security level is %d\n", level);
|
Logger::Print("Your current security level is %d\n", level);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -6,6 +6,8 @@ namespace Components
|
|||||||
|
|
||||||
bool Bans::IsBanned(Bans::Entry entry)
|
bool Bans::IsBanned(Bans::Entry entry)
|
||||||
{
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> _(Bans::AccessMutex);
|
||||||
|
|
||||||
Bans::BanList list;
|
Bans::BanList list;
|
||||||
Bans::LoadBans(&list);
|
Bans::LoadBans(&list);
|
||||||
|
|
||||||
@ -181,9 +183,9 @@ namespace Components
|
|||||||
Game::client_t* client = &Game::svs_clients[num];
|
Game::client_t* client = &Game::svs_clients[num];
|
||||||
|
|
||||||
SteamID guid;
|
SteamID guid;
|
||||||
guid.bits = client->steamid;
|
guid.bits = client->steamID;
|
||||||
|
|
||||||
Bans::InsertBan({ guid, client->addr.ip });
|
Bans::InsertBan({ guid, client->netchan.remoteAddress.ip });
|
||||||
|
|
||||||
Game::SV_KickClientError(client, reason);
|
Game::SV_KickClientError(client, reason);
|
||||||
}
|
}
|
||||||
@ -232,17 +234,17 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Command::Add("banclient", [](Command::Params* params)
|
Command::Add("banclient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->length() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
std::string reason = "EXE_ERR_BANNED_PERM";
|
std::string reason = "EXE_ERR_BANNED_PERM";
|
||||||
if (params->length() >= 3) reason = params->join(2);
|
if (params->size() >= 3) reason = params->join(2);
|
||||||
|
|
||||||
Bans::BanClientNum(atoi(params->get(1)), reason);
|
Bans::BanClientNum(atoi(params->get(1)), reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
Command::Add("unbanclient", [](Command::Params* params)
|
Command::Add("unbanclient", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->length() < 2) return;
|
if (params->size() < 2) return;
|
||||||
|
|
||||||
std::string type = params->get(1);
|
std::string type = params->get(1);
|
||||||
|
|
||||||
@ -272,9 +274,4 @@ namespace Components
|
|||||||
Bans::LoadBans(&list);
|
Bans::LoadBans(&list);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Bans::~Bans()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,6 @@ namespace Components
|
|||||||
typedef std::pair<SteamID, Game::netIP_t> Entry;
|
typedef std::pair<SteamID, Game::netIP_t> Entry;
|
||||||
|
|
||||||
Bans();
|
Bans();
|
||||||
~Bans();
|
|
||||||
|
|
||||||
static void BanClientNum(int num, const std::string& reason);
|
static void BanClientNum(int num, const std::string& reason);
|
||||||
static void UnbanClient(SteamID id);
|
static void UnbanClient(SteamID id);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
#define KEY_MASK_FIRE 1
|
#define KEY_MASK_FIRE 1
|
||||||
#define KEY_MASK_SPRINT 2
|
#define KEY_MASK_SPRINT 2
|
||||||
@ -96,7 +96,7 @@ namespace Components
|
|||||||
|
|
||||||
if (bots.exists())
|
if (bots.exists())
|
||||||
{
|
{
|
||||||
std::vector<std::string> names = Utils::String::Explode(bots.getBuffer(), '\n');
|
std::vector<std::string> names = Utils::String::Split(bots.getBuffer(), '\n');
|
||||||
|
|
||||||
for (auto name : names)
|
for (auto name : names)
|
||||||
{
|
{
|
||||||
@ -160,7 +160,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>)
|
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>)
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_INTEGER)
|
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_INTEGER)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1SetPing: Needs one integer parameter!\n");
|
Game::Scr_Error("^1SetPing: Needs one integer parameter!\n");
|
||||||
return;
|
return;
|
||||||
@ -250,7 +250,7 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>);
|
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>);
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1botWeapon: Needs one string parameter!\n");
|
Game::Scr_Error("^1botWeapon: Needs one string parameter!\n");
|
||||||
return;
|
return;
|
||||||
@ -293,7 +293,7 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>);
|
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>);
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
if (Game::Scr_GetNumParam() != 1u || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1botAction: Needs one string parameter!\n");
|
Game::Scr_Error("^1botAction: Needs one string parameter!\n");
|
||||||
return;
|
return;
|
||||||
@ -346,7 +346,7 @@ namespace Components
|
|||||||
|
|
||||||
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>);
|
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>);
|
||||||
{
|
{
|
||||||
if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER)
|
if (Game::Scr_GetNumParam() != 2u || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER)
|
||||||
{
|
{
|
||||||
Game::Scr_Error("^1botMovement: Needs two integer parameters!\n");
|
Game::Scr_Error("^1botMovement: Needs two integer parameters!\n");
|
||||||
return;
|
return;
|
||||||
@ -437,7 +437,7 @@ namespace Components
|
|||||||
ucmd.rightmove = g_botai[i].right;
|
ucmd.rightmove = g_botai[i].right;
|
||||||
ucmd.weapon = g_botai[i].weapon;
|
ucmd.weapon = g_botai[i].weapon;
|
||||||
|
|
||||||
client->deltaMessage = client->outgoingSequence - 1;
|
client->deltaMessage = client->netchan.outgoingSequence - 1;
|
||||||
|
|
||||||
Game::SV_ClientThink(client, &ucmd);
|
Game::SV_ClientThink(client, &ucmd);
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
unsigned int count = 1;
|
unsigned int count = 1;
|
||||||
|
|
||||||
if (params->length() > 1)
|
if (params->size() > 1)
|
||||||
{
|
{
|
||||||
if (params->get(1) == "all"s) count = static_cast<unsigned int>(-1);
|
if (params->get(1) == "all"s) count = static_cast<unsigned int>(-1);
|
||||||
else count = atoi(params->get(1));
|
else count = atoi(params->get(1));
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -192,14 +192,14 @@ namespace Components
|
|||||||
{
|
{
|
||||||
Dvar::OnInit([]()
|
Dvar::OnInit([]()
|
||||||
{
|
{
|
||||||
CardTitles::CustomTitleDvar = Dvar::Register<const char*>("customtitle", "", Game::dvar_flag::DVAR_FLAG_USERINFO | Game::dvar_flag::DVAR_FLAG_SAVED, "Custom card title");
|
CardTitles::CustomTitleDvar = Dvar::Register<const char*>("customtitle", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "Custom card title");
|
||||||
});
|
});
|
||||||
|
|
||||||
ServerCommands::OnCommand(21, [](Command::Params* params)
|
ServerCommands::OnCommand(21, [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->get(1) == "customTitles"s && !Dedicated::IsEnabled())
|
if (params->get(1) == "customTitles"s && !Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
if (params->length() == 3)
|
if (params->size() == 3)
|
||||||
{
|
{
|
||||||
CardTitles::ParseCustomTitles(params->get(2));
|
CardTitles::ParseCustomTitles(params->get(2));
|
||||||
return true;
|
return true;
|
||||||
@ -210,11 +210,6 @@ namespace Components
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(CardTitles::CustomTitles); ++i)
|
|
||||||
{
|
|
||||||
CardTitles::CustomTitles[i].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils::Hook(0x62EB26, CardTitles::GetPlayerCardClientInfoStub).install()->quick();
|
Utils::Hook(0x62EB26, CardTitles::GetPlayerCardClientInfoStub).install()->quick();
|
||||||
|
|
||||||
// Table lookup stuff
|
// Table lookup stuff
|
||||||
@ -227,12 +222,4 @@ namespace Components
|
|||||||
AntiCheat::CheckStartupTime();
|
AntiCheat::CheckStartupTime();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
CardTitles::~CardTitles()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < ARRAYSIZE(CardTitles::CustomTitles); ++i)
|
|
||||||
{
|
|
||||||
CardTitles::CustomTitles[i].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,6 @@ namespace Components
|
|||||||
static void ParseCustomTitles(const char * msg);
|
static void ParseCustomTitles(const char * msg);
|
||||||
|
|
||||||
CardTitles();
|
CardTitles();
|
||||||
~CardTitles();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static CClient * GetClientByIndex(std::uint32_t index);
|
static CClient * GetClientByIndex(std::uint32_t index);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -19,7 +19,7 @@ namespace Components
|
|||||||
data = "^1Unable to get changelog.";
|
data = "^1Unable to get changelog.";
|
||||||
}
|
}
|
||||||
|
|
||||||
Changelog::Lines = Utils::String::Explode(data, '\n');
|
Changelog::Lines = Utils::String::Split(data, '\n');
|
||||||
|
|
||||||
for (auto& line : Changelog::Lines)
|
for (auto& line : Changelog::Lines)
|
||||||
{
|
{
|
||||||
@ -60,12 +60,4 @@ namespace Components
|
|||||||
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner1);
|
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Changelog::~Changelog()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> _(Changelog::Mutex);
|
|
||||||
Changelog::Lines.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ namespace Components
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Changelog();
|
Changelog();
|
||||||
~Changelog();
|
|
||||||
|
|
||||||
static void LoadChangelog();
|
static void LoadChangelog();
|
||||||
|
|
||||||
|
331
src/Components/Modules/Chat.cpp
Normal file
331
src/Components/Modules/Chat.cpp
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast<Game::dvar_t**>(0x7ED398);
|
||||||
|
Dvar::Var Chat::cg_chatWidth;
|
||||||
|
Game::dvar_t** Chat::cg_chatTime = reinterpret_cast<Game::dvar_t**>(0x9F5DE8);
|
||||||
|
|
||||||
|
bool Chat::SendChat;
|
||||||
|
|
||||||
|
std::mutex Chat::AccessMutex;
|
||||||
|
std::unordered_set<std::uint64_t> Chat::MuteList;
|
||||||
|
|
||||||
|
const char* Chat::EvaluateSay(char* text, Game::gentity_t* player)
|
||||||
|
{
|
||||||
|
Chat::SendChat = true;
|
||||||
|
|
||||||
|
if (text[1] == '/')
|
||||||
|
{
|
||||||
|
Chat::SendChat = false;
|
||||||
|
text[1] = text[0];
|
||||||
|
++text;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> lock(Chat::AccessMutex);
|
||||||
|
if (Chat::MuteList.find(Game::svs_clients[player->s.number].steamID) != Chat::MuteList.end())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
Chat::SendChat = false;
|
||||||
|
Game::SV_GameSendServerCommand(player->s.number, 0,
|
||||||
|
Utils::String::VA("%c \"You are muted\"", 0x65));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test whether the lock is still locked
|
||||||
|
if (lock.owns_lock())
|
||||||
|
{
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextRenderer::StripMaterialTextIcons(text, text, strlen(text) + 1);
|
||||||
|
|
||||||
|
Game::Scr_AddEntity(player);
|
||||||
|
Game::Scr_AddString(text + 1);
|
||||||
|
Game::Scr_NotifyLevel(Game::SL_GetString("say", 0), 2);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Chat::PreSayStub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax, [esp + 100h + 10h]
|
||||||
|
|
||||||
|
push eax
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push[esp + 100h + 28h]
|
||||||
|
push eax
|
||||||
|
call Chat::EvaluateSay
|
||||||
|
add esp, 8h
|
||||||
|
|
||||||
|
mov[esp + 20h], eax
|
||||||
|
popad
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
mov[esp + 100h + 10h], eax
|
||||||
|
|
||||||
|
jmp PlayerName::CleanStrStub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Chat::PostSayStub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
// eax is used by the callee
|
||||||
|
push eax
|
||||||
|
|
||||||
|
xor eax, eax
|
||||||
|
mov al, Chat::SendChat
|
||||||
|
|
||||||
|
test al, al
|
||||||
|
jnz return
|
||||||
|
|
||||||
|
// Don't send the chat
|
||||||
|
pop eax
|
||||||
|
retn
|
||||||
|
|
||||||
|
return:
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
// Jump to the target
|
||||||
|
push 5DF620h
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::CheckChatLineEnd(const char*& inputBuffer, char*& lineBuffer, float& len, const int chatHeight, const float chatWidth, char*& lastSpacePos, char*& lastFontIconPos, const int lastColor)
|
||||||
|
{
|
||||||
|
if (len > chatWidth)
|
||||||
|
{
|
||||||
|
if (lastSpacePos && lastSpacePos > lastFontIconPos)
|
||||||
|
{
|
||||||
|
inputBuffer += lastSpacePos - lineBuffer + 1;
|
||||||
|
lineBuffer = lastSpacePos;
|
||||||
|
}
|
||||||
|
else if (lastFontIconPos)
|
||||||
|
{
|
||||||
|
inputBuffer += lastFontIconPos - lineBuffer;
|
||||||
|
lineBuffer = lastFontIconPos;
|
||||||
|
}
|
||||||
|
*lineBuffer = 0;
|
||||||
|
len = 0.0f;
|
||||||
|
Game::cgsArray[0].teamChatMsgTimes[Game::cgsArray[0].teamChatPos % chatHeight] = Game::cgArray[0].time;
|
||||||
|
|
||||||
|
Game::cgsArray[0].teamChatPos++;
|
||||||
|
lineBuffer = Game::cgsArray[0].teamChatMsgs[Game::cgsArray[0].teamChatPos % chatHeight];
|
||||||
|
lineBuffer[0] = '^';
|
||||||
|
lineBuffer[1] = CharForColorIndex(lastColor);
|
||||||
|
lineBuffer += 2;
|
||||||
|
lastSpacePos = nullptr;
|
||||||
|
lastFontIconPos = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::CG_AddToTeamChat(const char* text)
|
||||||
|
{
|
||||||
|
// Text can only be 150 characters maximum. This is bigger than the teamChatMsgs buffers with 160 characters
|
||||||
|
// Therefore it is not needed to check for buffer lengths
|
||||||
|
|
||||||
|
const auto chatHeight = (*cg_chatHeight)->current.integer;
|
||||||
|
const auto chatWidth = static_cast<float>(cg_chatWidth.get<int>());
|
||||||
|
const auto chatTime = (*cg_chatTime)->current.integer;
|
||||||
|
if (chatHeight < 0 || static_cast<unsigned>(chatHeight) > std::extent_v<decltype(Game::cgs_t::teamChatMsgs)> || chatWidth <= 0 || chatTime <= 0)
|
||||||
|
{
|
||||||
|
Game::cgsArray[0].teamLastChatPos = 0;
|
||||||
|
Game::cgsArray[0].teamChatPos = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextRenderer::FontIconInfo fontIconInfo{};
|
||||||
|
auto len = 0.0f;
|
||||||
|
auto lastColor = static_cast<int>(TEXT_COLOR_DEFAULT);
|
||||||
|
char* lastSpace = nullptr;
|
||||||
|
char* lastFontIcon = nullptr;
|
||||||
|
char* p = Game::cgsArray[0].teamChatMsgs[Game::cgsArray[0].teamChatPos % chatHeight];
|
||||||
|
p[0] = '\0';
|
||||||
|
|
||||||
|
while (*text)
|
||||||
|
{
|
||||||
|
CheckChatLineEnd(text, p, len, chatHeight, chatWidth, lastSpace, lastFontIcon, lastColor);
|
||||||
|
|
||||||
|
const char* fontIconEndPos = &text[1];
|
||||||
|
if(text[0] == TextRenderer::FONT_ICON_SEPARATOR_CHARACTER && TextRenderer::IsFontIcon(fontIconEndPos, fontIconInfo))
|
||||||
|
{
|
||||||
|
// The game calculates width on a per character base. Since the width of a font icon is calculated based on the height of the font
|
||||||
|
// which is roughly double as much as the average width of a character without an additional multiplier the calculated len of the font icon
|
||||||
|
// would be less than it most likely would be rendered. Therefore apply a guessed 2.0f multiplier at this location which makes
|
||||||
|
// the calculated width of a font icon roughly comparable to the width of an average character of the font.
|
||||||
|
const auto normalizedFontIconWidth = TextRenderer::GetNormalizedFontIconWidth(fontIconInfo);
|
||||||
|
const auto fontIconWidth = normalizedFontIconWidth * FONT_ICON_CHAT_WIDTH_CALCULATION_MULTIPLIER;
|
||||||
|
len += fontIconWidth;
|
||||||
|
|
||||||
|
lastFontIcon = p;
|
||||||
|
for(; text < fontIconEndPos; text++)
|
||||||
|
{
|
||||||
|
p[0] = text[0];
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckChatLineEnd(text, p, len, chatHeight, chatWidth, lastSpace, lastFontIcon, lastColor);
|
||||||
|
}
|
||||||
|
else if (text[0] == '^' && text[1] != 0 && text[1] >= TextRenderer::COLOR_FIRST_CHAR && text[1] <= TextRenderer::COLOR_LAST_CHAR)
|
||||||
|
{
|
||||||
|
p[0] = '^';
|
||||||
|
p[1] = text[1];
|
||||||
|
lastColor = ColorIndexForChar(text[1]);
|
||||||
|
p += 2;
|
||||||
|
text += 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (text[0] == ' ')
|
||||||
|
lastSpace = p;
|
||||||
|
*p++ = *text++;
|
||||||
|
len += 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*p = 0;
|
||||||
|
|
||||||
|
Game::cgsArray[0].teamChatMsgTimes[Game::cgsArray[0].teamChatPos % chatHeight] = Game::cgArray[0].time;
|
||||||
|
|
||||||
|
Game::cgsArray[0].teamChatPos++;
|
||||||
|
if (Game::cgsArray[0].teamChatPos - Game::cgsArray[0].teamLastChatPos > chatHeight)
|
||||||
|
Game::cgsArray[0].teamLastChatPos = Game::cgsArray[0].teamChatPos + 1 - chatHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void Chat::CG_AddToTeamChat_Stub()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
pushad
|
||||||
|
|
||||||
|
push ecx
|
||||||
|
call CG_AddToTeamChat
|
||||||
|
add esp, 4h
|
||||||
|
|
||||||
|
popad
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::MuteClient(const Game::client_t* client)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Chat::AccessMutex);
|
||||||
|
|
||||||
|
if (Chat::MuteList.find(client->steamID) == Chat::MuteList.end())
|
||||||
|
{
|
||||||
|
Chat::MuteList.insert(client->steamID);
|
||||||
|
lock.unlock();
|
||||||
|
|
||||||
|
Logger::Print("%s was muted\n", client->name);
|
||||||
|
Game::SV_GameSendServerCommand(client->gentity->s.number, 0,
|
||||||
|
Utils::String::VA("%c \"You were muted\"", 0x65));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.unlock();
|
||||||
|
Logger::Print("%s is already muted\n", client->name);
|
||||||
|
Game::SV_GameSendServerCommand(-1, 0,
|
||||||
|
Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::UnmuteClient(const Game::client_t* client)
|
||||||
|
{
|
||||||
|
Chat::UnmuteInternal(client->steamID);
|
||||||
|
|
||||||
|
Logger::Print("%s was unmuted\n", client->name);
|
||||||
|
Game::SV_GameSendServerCommand(client->gentity->s.number, 0,
|
||||||
|
Utils::String::VA("%c \"You were unmuted\"", 0x65));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::UnmuteInternal(const std::uint64_t id, bool everyone)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(Chat::AccessMutex);
|
||||||
|
|
||||||
|
if (everyone)
|
||||||
|
Chat::MuteList.clear();
|
||||||
|
else
|
||||||
|
Chat::MuteList.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Chat::AddChatCommands()
|
||||||
|
{
|
||||||
|
Command::AddSV("muteClient", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* cmd = params->get(0);
|
||||||
|
if (params->size() < 2)
|
||||||
|
{
|
||||||
|
Logger::Print("Usage: %s <client number> : prevent the player from using the chat\n", cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* client = Game::SV_GetPlayerByNum();
|
||||||
|
if (client != nullptr)
|
||||||
|
{
|
||||||
|
Chat::MuteClient(client);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Command::AddSV("unmute", [](Command::Params* params)
|
||||||
|
{
|
||||||
|
if (!Dvar::Var("sv_running").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("Server is not running.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* cmd = params->get(0);
|
||||||
|
if (params->size() < 2)
|
||||||
|
{
|
||||||
|
Logger::Print("Usage: %s <client number or guid>\n%s all = unmute everyone\n", cmd, cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto* client = Game::SV_GetPlayerByNum();
|
||||||
|
|
||||||
|
if (client != nullptr)
|
||||||
|
{
|
||||||
|
Chat::UnmuteClient(client);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::strcmp(params->get(1), "all") == 0)
|
||||||
|
{
|
||||||
|
Logger::Print("All players were unmuted\n");
|
||||||
|
Chat::UnmuteInternal(0, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto steamId = std::strtoull(params->get(1), nullptr, 16);
|
||||||
|
Chat::UnmuteInternal(steamId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Chat::Chat()
|
||||||
|
{
|
||||||
|
Dvar::OnInit([]
|
||||||
|
{
|
||||||
|
cg_chatWidth = Dvar::Register<int>("cg_chatWidth", 52, 1, std::numeric_limits<int>::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message");
|
||||||
|
Chat::AddChatCommands();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Intercept chat sending
|
||||||
|
Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x4D00D4, PostSayStub, HOOK_CALL).install()->quick();
|
||||||
|
Utils::Hook(0x4D0110, PostSayStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
// Change logic that does word splitting with new lines for chat messages to support fonticons
|
||||||
|
Utils::Hook(0x592E10, CG_AddToTeamChat_Stub, HOOK_JUMP).install()->quick();
|
||||||
|
}
|
||||||
|
}
|
35
src/Components/Modules/Chat.hpp
Normal file
35
src/Components/Modules/Chat.hpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class Chat : public Component
|
||||||
|
{
|
||||||
|
static constexpr auto FONT_ICON_CHAT_WIDTH_CALCULATION_MULTIPLIER = 2.0f;
|
||||||
|
public:
|
||||||
|
Chat();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static Game::dvar_t** cg_chatHeight;
|
||||||
|
static Dvar::Var cg_chatWidth;
|
||||||
|
static Game::dvar_t** cg_chatTime;
|
||||||
|
|
||||||
|
static bool SendChat;
|
||||||
|
|
||||||
|
static std::mutex AccessMutex;
|
||||||
|
static std::unordered_set<std::uint64_t> MuteList;
|
||||||
|
|
||||||
|
static const char* EvaluateSay(char* text, Game::gentity_t* player);
|
||||||
|
|
||||||
|
static void PreSayStub();
|
||||||
|
static void PostSayStub();
|
||||||
|
|
||||||
|
static void CheckChatLineEnd(const char*& inputBuffer, char*& lineBuffer, float& len, int chatHeight, float chatWidth, char*& lastSpacePos, char*& lastFontIconPos, int lastColor);
|
||||||
|
static void CG_AddToTeamChat(const char* text);
|
||||||
|
static void CG_AddToTeamChat_Stub();
|
||||||
|
|
||||||
|
static void MuteClient(const Game::client_t* client);
|
||||||
|
static void UnmuteClient(const Game::client_t* client);
|
||||||
|
static void UnmuteInternal(const std::uint64_t id, bool everyone = false);
|
||||||
|
static void AddChatCommands();
|
||||||
|
};
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -75,7 +75,7 @@ namespace Components
|
|||||||
// Create clantag dvar
|
// Create clantag dvar
|
||||||
Dvar::OnInit([]()
|
Dvar::OnInit([]()
|
||||||
{
|
{
|
||||||
Dvar::Register<const char*>("clantag", "", Game::dvar_flag::DVAR_FLAG_USERINFO | Game::dvar_flag::DVAR_FLAG_SAVED, "If set, your clantag will be shown on the scoreboard.");
|
Dvar::Register<const char*>("clantag", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "If set, your clantag will be shown on the scoreboard.");
|
||||||
});
|
});
|
||||||
|
|
||||||
// Servercommand hook
|
// Servercommand hook
|
||||||
@ -83,7 +83,7 @@ namespace Components
|
|||||||
{
|
{
|
||||||
if (params->get(1) == "clantags"s && !Dedicated::IsEnabled())
|
if (params->get(1) == "clantags"s && !Dedicated::IsEnabled())
|
||||||
{
|
{
|
||||||
if (params->length() == 3)
|
if (params->size() == 3)
|
||||||
{
|
{
|
||||||
ClanTags::ParseClantags(params->get(2));
|
ClanTags::ParseClantags(params->get(2));
|
||||||
return true;
|
return true;
|
||||||
@ -93,20 +93,7 @@ namespace Components
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
for (int i = 0; i < ARRAYSIZE(ClanTags::Tags); ++i)
|
|
||||||
{
|
|
||||||
ClanTags::Tags[i].clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw clantag before playername
|
// Draw clantag before playername
|
||||||
Utils::Hook(0x591242, ClanTags::DrawPlayerNameOnScoreboard).install()->quick();
|
Utils::Hook(0x591242, ClanTags::DrawPlayerNameOnScoreboard).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClanTags::~ClanTags()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < ARRAYSIZE(ClanTags::Tags); ++i)
|
|
||||||
{
|
|
||||||
ClanTags::Tags[i].clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ namespace Components
|
|||||||
static const char* GetUserClantag(std::uint32_t clientnum, const char * playername);
|
static const char* GetUserClantag(std::uint32_t clientnum, const char * playername);
|
||||||
|
|
||||||
ClanTags();
|
ClanTags();
|
||||||
~ClanTags();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::string Tags[18];
|
static std::string Tags[18];
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
@ -134,7 +134,7 @@ namespace Components
|
|||||||
|
|
||||||
if (client->state >= 3)
|
if (client->state >= 3)
|
||||||
{
|
{
|
||||||
std::string ip = Game::NET_AdrToString(client->addr);
|
std::string ip = Game::NET_AdrToString(client->netchan.remoteAddress);
|
||||||
if (ip.find_first_of(":") != std::string::npos)
|
if (ip.find_first_of(":") != std::string::npos)
|
||||||
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
|
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
|
||||||
Game::Scr_AddString(ip.data());
|
Game::Scr_AddString(ip.data());
|
||||||
|
312
src/Components/Modules/ClientCommand.cpp
Normal file
312
src/Components/Modules/ClientCommand.cpp
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
std::unordered_map<std::string, Utils::Slot<ClientCommand::Callback>> ClientCommand::FunctionMap;
|
||||||
|
|
||||||
|
bool ClientCommand::CheatsOk(const Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
|
||||||
|
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||||
|
{
|
||||||
|
Logger::Print("CheatsOk: cheats are disabled!\n");
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->health < 1)
|
||||||
|
{
|
||||||
|
Logger::Print("CheatsOk: entity %u must be alive to use this command!\n", entNum);
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClientCommand::CallbackHandler(Game::gentity_s* ent, const char* cmd)
|
||||||
|
{
|
||||||
|
const auto command = Utils::String::ToLower(cmd);
|
||||||
|
|
||||||
|
if (ClientCommand::FunctionMap.find(command) != ClientCommand::FunctionMap.end())
|
||||||
|
{
|
||||||
|
ClientCommand::FunctionMap[command](ent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::Add(const char* name, Utils::Slot<Callback> callback)
|
||||||
|
{
|
||||||
|
const auto command = Utils::String::ToLower(name);
|
||||||
|
|
||||||
|
ClientCommand::FunctionMap[command] = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::ClientCommandStub(const int clientNum)
|
||||||
|
{
|
||||||
|
char cmd[1024]{};
|
||||||
|
const auto entity = &Game::g_entities[clientNum];
|
||||||
|
|
||||||
|
if (entity->client == nullptr)
|
||||||
|
{
|
||||||
|
Logger::Print("ClientCommand: client %d is not fully in game yet\n", clientNum);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::SV_Cmd_ArgvBuffer(0, cmd, sizeof(cmd));
|
||||||
|
|
||||||
|
if (!ClientCommand::CallbackHandler(entity, cmd))
|
||||||
|
{
|
||||||
|
// If no callback was found call original game function
|
||||||
|
Utils::Hook::Call<void(const int)>(0x416790)(clientNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::AddCheatCommands()
|
||||||
|
{
|
||||||
|
ClientCommand::Add("noclip", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||||
|
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
Logger::Print("Noclip toggled for entity %u\n", entNum);
|
||||||
|
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
|
(ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF"));
|
||||||
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("ufo", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||||
|
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
Logger::Print("UFO toggled for entity %u\n", entNum);
|
||||||
|
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
|
(ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF"));
|
||||||
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("god", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->flags ^= Game::FL_GODMODE;
|
||||||
|
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
Logger::Print("God toggled for entity %u\n", entNum);
|
||||||
|
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
|
(ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF"));
|
||||||
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("demigod", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->flags ^= Game::FL_DEMI_GODMODE;
|
||||||
|
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
Logger::Print("Demigod toggled for entity %u\n", entNum);
|
||||||
|
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
|
(ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF"));
|
||||||
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("notarget", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->flags ^= Game::FL_NOTARGET;
|
||||||
|
|
||||||
|
const auto entNum = ent->s.number;
|
||||||
|
Logger::Print("Notarget toggled for entity %u\n", entNum);
|
||||||
|
|
||||||
|
Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65,
|
||||||
|
(ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF"));
|
||||||
|
});
|
||||||
|
|
||||||
|
ClientCommand::Add("setviewpos", [](Game::gentity_s* ent)
|
||||||
|
{
|
||||||
|
assert(ent != nullptr);
|
||||||
|
|
||||||
|
if (!ClientCommand::CheatsOk(ent))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Command::ServerParams params = {};
|
||||||
|
Game::vec3_t origin, angles{0.f, 0.f, 0.f};
|
||||||
|
|
||||||
|
if (params.size() < 4 || params.size() > 6)
|
||||||
|
{
|
||||||
|
Game::SV_GameSendServerCommand(ent->s.number, 0,
|
||||||
|
Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
origin[i] = std::strtof(params.get(i + 1), nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() >= 5)
|
||||||
|
{
|
||||||
|
angles[1] = std::strtof(params.get(4), nullptr); // Yaw
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.size() == 6)
|
||||||
|
{
|
||||||
|
angles[0] = std::strtof(params.get(5), nullptr); // Pitch
|
||||||
|
}
|
||||||
|
|
||||||
|
Game::TeleportPlayer(ent, origin, angles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientCommand::AddScriptFunctions()
|
||||||
|
{
|
||||||
|
Script::AddFunction("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(<optional int toggle>);
|
||||||
|
{
|
||||||
|
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1NoClip: entity %u is not a client\n", entref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetInt(0))
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_NOCLIP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_NOCLIP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(<optional int toggle>);
|
||||||
|
{
|
||||||
|
if (entref >= Game::MAX_GENTITIES || Game::g_entities[entref].client == nullptr)
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1Ufo: entity %u is not a client\n", entref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetInt(0))
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags |= Game::PLAYER_FLAG_UFO;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags &= ~Game::PLAYER_FLAG_UFO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("God", [](Game::scr_entref_t entref) // gsc: God(<optional int toggle>);
|
||||||
|
{
|
||||||
|
if (entref >= Game::MAX_GENTITIES)
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1God: entity %u is out of bounds\n", entref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetInt(0))
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags |= Game::FL_GODMODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags &= ~Game::FL_GODMODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags ^= Game::FL_GODMODE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(<optional int toggle>);
|
||||||
|
{
|
||||||
|
if (entref >= Game::MAX_GENTITIES)
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1Demigod: entity %u is out of bounds\n", entref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetInt(0))
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags |= Game::FL_DEMI_GODMODE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags &= ~Game::FL_DEMI_GODMODE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags ^= Game::FL_DEMI_GODMODE;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Script::AddFunction("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(<optional int toggle>);
|
||||||
|
{
|
||||||
|
if (entref >= Game::MAX_GENTITIES)
|
||||||
|
{
|
||||||
|
Game::Scr_Error(Utils::String::VA("^1Notarget: entity %u is out of bounds\n", entref));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Game::Scr_GetNumParam() == 1u && Game::Scr_GetType(0) == Game::VAR_INTEGER)
|
||||||
|
{
|
||||||
|
if (Game::Scr_GetInt(0))
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags |= Game::FL_NOTARGET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags &= ~Game::FL_NOTARGET;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Game::g_entities[entref].flags ^= Game::FL_NOTARGET;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientCommand::ClientCommand()
|
||||||
|
{
|
||||||
|
// Hook call to ClientCommand in SV_ExecuteClientCommand so we may add custom commands
|
||||||
|
Utils::Hook(0x6259FA, ClientCommand::ClientCommandStub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
ClientCommand::AddCheatCommands();
|
||||||
|
ClientCommand::AddScriptFunctions();
|
||||||
|
}
|
||||||
|
}
|
23
src/Components/Modules/ClientCommand.hpp
Normal file
23
src/Components/Modules/ClientCommand.hpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Components
|
||||||
|
{
|
||||||
|
class ClientCommand : public Component
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef void(Callback)(Game::gentity_s* entity);
|
||||||
|
|
||||||
|
ClientCommand();
|
||||||
|
|
||||||
|
static void Add(const char* name, Utils::Slot<Callback> callback);
|
||||||
|
static bool CheatsOk(const Game::gentity_s* ent);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::unordered_map<std::string, Utils::Slot<Callback>> FunctionMap;
|
||||||
|
|
||||||
|
static bool CallbackHandler(Game::gentity_s* ent, const char* cmd);
|
||||||
|
static void ClientCommandStub(const int clientNum);
|
||||||
|
static void AddCheatCommands();
|
||||||
|
static void AddScriptFunctions();
|
||||||
|
};
|
||||||
|
}
|
@ -1,269 +0,0 @@
|
|||||||
#include "STDInclude.hpp"
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
Dvar::Var Colors::NewColors;
|
|
||||||
std::vector<DWORD> Colors::ColorTable;
|
|
||||||
|
|
||||||
DWORD Colors::HsvToRgb(Colors::HsvColor hsv)
|
|
||||||
{
|
|
||||||
DWORD rgb;
|
|
||||||
unsigned char region, p, q, t;
|
|
||||||
unsigned int h, s, v, remainder;
|
|
||||||
|
|
||||||
if (hsv.s == 0)
|
|
||||||
{
|
|
||||||
rgb = RGB(hsv.v, hsv.v, hsv.v);
|
|
||||||
return rgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
// converting to 16 bit to prevent overflow
|
|
||||||
h = hsv.h;
|
|
||||||
s = hsv.s;
|
|
||||||
v = hsv.v;
|
|
||||||
|
|
||||||
region = static_cast<uint8_t>(h / 43);
|
|
||||||
remainder = (h - (region * 43)) * 6;
|
|
||||||
|
|
||||||
p = static_cast<uint8_t>((v * (255 - s)) >> 8);
|
|
||||||
q = static_cast<uint8_t>((v * (255 - ((s * remainder) >> 8))) >> 8);
|
|
||||||
t = static_cast<uint8_t>((v * (255 - ((s * (255 - remainder)) >> 8))) >> 8);
|
|
||||||
|
|
||||||
switch (region)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
rgb = RGB(v, t, p);
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
rgb = RGB(q, v, p);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
rgb = RGB(p, v, t);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
rgb = RGB(p, q, v);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
rgb = RGB(t, p, v);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
rgb = RGB(v, p, q);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rgb;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Colors::Strip(const char* in, char* out, int max)
|
|
||||||
{
|
|
||||||
if (!in || !out) return;
|
|
||||||
|
|
||||||
max--;
|
|
||||||
int current = 0;
|
|
||||||
while (*in != 0 && current < max)
|
|
||||||
{
|
|
||||||
char index = *(in + 1);
|
|
||||||
if (*in == '^' && (Colors::ColorIndex(index) != 7 || index == '7'))
|
|
||||||
{
|
|
||||||
++in;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*out = *in;
|
|
||||||
++out;
|
|
||||||
++current;
|
|
||||||
}
|
|
||||||
|
|
||||||
++in;
|
|
||||||
}
|
|
||||||
*out = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Colors::Strip(const std::string& in)
|
|
||||||
{
|
|
||||||
char buffer[1000] = { 0 }; // Should be more than enough
|
|
||||||
Colors::Strip(in.data(), buffer, sizeof(buffer));
|
|
||||||
return std::string(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Colors::UserInfoCopy(char* buffer, const char* name, size_t size)
|
|
||||||
{
|
|
||||||
Utils::Memory::Allocator allocator;
|
|
||||||
|
|
||||||
if (!Dvar::Var("sv_allowColoredNames").get<bool>())
|
|
||||||
{
|
|
||||||
Colors::Strip(name, buffer, size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
strncpy_s(buffer, size, name, _TRUNCATE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Colors::ClientUserinfoChanged()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax, [esp + 4h] // length
|
|
||||||
//sub eax, 1
|
|
||||||
push eax
|
|
||||||
|
|
||||||
push ecx // name
|
|
||||||
push edx // buffer
|
|
||||||
|
|
||||||
call Colors::UserInfoCopy
|
|
||||||
|
|
||||||
add esp, 0Ch
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* Colors::GetClientName(int localClientNum, int index, char *buf, size_t size)
|
|
||||||
{
|
|
||||||
Game::CL_GetClientName(localClientNum, index, buf, size);
|
|
||||||
|
|
||||||
// Append clantag to username & remove the colors
|
|
||||||
strncpy_s(buf, size, Colors::Strip(ClanTags::GetUserClantag(index, buf)).data(), size);
|
|
||||||
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Colors::PatchColorLimit(char limit)
|
|
||||||
{
|
|
||||||
Utils::Hook::Set<char>(0x535629, limit); // DrawText2d
|
|
||||||
Utils::Hook::Set<char>(0x4C1BE4, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x4863DD, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x486429, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x49A5A8, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x505721, limit); // R_TextWidth
|
|
||||||
Utils::Hook::Set<char>(0x505801, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x50597F, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x5815DB, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x592ED0, limit); // No idea :P
|
|
||||||
Utils::Hook::Set<char>(0x5A2E2E, limit); // No idea :P
|
|
||||||
|
|
||||||
Utils::Hook::Set<char>(0x5A2733, limit - '0'); // No idea :P
|
|
||||||
}
|
|
||||||
|
|
||||||
char Colors::Add(uint8_t r, uint8_t g, uint8_t b)
|
|
||||||
{
|
|
||||||
char index = '0' + static_cast<char>(Colors::ColorTable.size());
|
|
||||||
Colors::ColorTable.push_back(RGB(r, g, b));
|
|
||||||
Colors::PatchColorLimit(index);
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int Colors::ColorIndex(char index)
|
|
||||||
{
|
|
||||||
char result = index - '0';
|
|
||||||
if (static_cast<unsigned int>(result) >= Colors::ColorTable.size() || result < 0) result = 7;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Colors::LookupColor(DWORD* color, char index)
|
|
||||||
{
|
|
||||||
*color = RGB(255, 255, 255);
|
|
||||||
|
|
||||||
if (index == '8') // Color 8
|
|
||||||
{
|
|
||||||
*color = *reinterpret_cast<DWORD*>(0x66E5F70);
|
|
||||||
}
|
|
||||||
else if (index == '9') // Color 9
|
|
||||||
{
|
|
||||||
*color = *reinterpret_cast<DWORD*>(0x66E5F74);
|
|
||||||
}
|
|
||||||
else if (index == ':')
|
|
||||||
{
|
|
||||||
*color = Colors::HsvToRgb({ static_cast<uint8_t>((Game::Sys_Milliseconds() / 200) % 256), 255,255 });
|
|
||||||
}
|
|
||||||
else if (index == ';')
|
|
||||||
{
|
|
||||||
float fltColor[4];
|
|
||||||
Game::Dvar_GetUnpackedColorByName("sv_customTextColor", fltColor);
|
|
||||||
*color = RGB(fltColor[0] * 255, fltColor[1] * 255, fltColor[2] * 255);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int clrIndex = Colors::ColorIndex(index);
|
|
||||||
|
|
||||||
// Use native colors
|
|
||||||
if (clrIndex <= 7 && !Colors::NewColors.get<bool>())
|
|
||||||
{
|
|
||||||
*color = reinterpret_cast<DWORD*>(0x78DC70)[index - 48];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*color = Colors::ColorTable[clrIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* Colors::CleanStrStub(char* string)
|
|
||||||
{
|
|
||||||
Colors::Strip(string, string, strlen(string) + 1);
|
|
||||||
return string;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) void Colors::LookupColorStub()
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
pushad
|
|
||||||
push [esp + 24h] // Index
|
|
||||||
push esi // Color ref
|
|
||||||
call Colors::LookupColor
|
|
||||||
add esp, 8h
|
|
||||||
popad
|
|
||||||
retn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Colors::Colors()
|
|
||||||
{
|
|
||||||
// Disable SV_UpdateUserinfo_f, to block changing the name ingame
|
|
||||||
Utils::Hook::Set<BYTE>(0x6258D0, 0xC3);
|
|
||||||
|
|
||||||
// Allow colored names ingame
|
|
||||||
Utils::Hook(0x5D8B40, Colors::ClientUserinfoChanged, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// Though, don't apply that to overhead names.
|
|
||||||
Utils::Hook(0x581932, Colors::GetClientName, HOOK_CALL).install()->quick();
|
|
||||||
|
|
||||||
// Patch RB_LookupColor
|
|
||||||
Utils::Hook(0x534CD0, Colors::LookupColorStub, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// Patch ColorIndex
|
|
||||||
Utils::Hook(0x417770, Colors::ColorIndex, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// Patch I_CleanStr
|
|
||||||
Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick();
|
|
||||||
|
|
||||||
// Register dvar
|
|
||||||
Colors::NewColors = Dvar::Register<bool>("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare² color code style.");
|
|
||||||
Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_FLAG_REPLICATED, "Color for the extended color code.");
|
|
||||||
Dvar::Register<bool>("sv_allowColoredNames", true, Game::dvar_flag::DVAR_FLAG_NONE, "Allow colored names on the server");
|
|
||||||
|
|
||||||
// Add our colors
|
|
||||||
Colors::Add(0, 0, 0); // 0 - Black
|
|
||||||
Colors::Add(255, 49, 49); // 1 - Red
|
|
||||||
Colors::Add(134, 192, 0); // 2 - Green
|
|
||||||
Colors::Add(255, 173, 34); // 3 - Yellow
|
|
||||||
Colors::Add(0, 135, 193); // 4 - Blue
|
|
||||||
Colors::Add(32, 197, 255); // 5 - Light Blue
|
|
||||||
Colors::Add(151, 80, 221); // 6 - Pink
|
|
||||||
|
|
||||||
Colors::Add(255, 255, 255); // 7 - White
|
|
||||||
|
|
||||||
Colors::Add(0, 0, 0); // 8 - Team color (axis?)
|
|
||||||
Colors::Add(0, 0, 0); // 9 - Team color (allies?)
|
|
||||||
|
|
||||||
// Custom colors
|
|
||||||
Colors::Add(0, 0, 0); // 10 - Rainbow (:)
|
|
||||||
Colors::Add(0, 0, 0); // 11 - Server color (;) - using that color in infostrings (e.g. your name) fails, ';' is an illegal character!
|
|
||||||
}
|
|
||||||
|
|
||||||
Colors::~Colors()
|
|
||||||
{
|
|
||||||
Colors::ColorTable.clear();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace Components
|
|
||||||
{
|
|
||||||
class Colors : public Component
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Colors();
|
|
||||||
~Colors();
|
|
||||||
|
|
||||||
static void Strip(const char* in, char* out, int max);
|
|
||||||
static std::string Strip(const std::string& in);
|
|
||||||
|
|
||||||
static char Add(uint8_t r, uint8_t g, uint8_t b);
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct HsvColor
|
|
||||||
{
|
|
||||||
unsigned char h;
|
|
||||||
unsigned char s;
|
|
||||||
unsigned char v;
|
|
||||||
};
|
|
||||||
|
|
||||||
static Dvar::Var NewColors;
|
|
||||||
|
|
||||||
static DWORD HsvToRgb(HsvColor hsv);
|
|
||||||
|
|
||||||
static void UserInfoCopy(char* buffer, const char* name, size_t size);
|
|
||||||
|
|
||||||
static void ClientUserinfoChanged();
|
|
||||||
static char* GetClientName(int localClientNum, int index, char *buf, size_t size);
|
|
||||||
static void PatchColorLimit(char limit);
|
|
||||||
|
|
||||||
static unsigned int ColorIndex(char index);
|
|
||||||
static void LookupColor(DWORD* color, char index);
|
|
||||||
static void LookupColorStub();
|
|
||||||
static char* CleanStrStub(char* string);
|
|
||||||
static std::vector<DWORD> ColorTable;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,60 +1,75 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMap;
|
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMap;
|
||||||
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMapSV;
|
std::unordered_map<std::string, Utils::Slot<Command::Callback>> Command::FunctionMapSV;
|
||||||
|
|
||||||
std::string Command::Params::join(size_t startIndex)
|
std::string Command::Params::join(const int index)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
for (size_t i = startIndex; i < this->length(); ++i)
|
for (auto i = index; i < this->size(); i++)
|
||||||
{
|
{
|
||||||
if (i > startIndex) result.append(" ");
|
if (i > index) result.append(" ");
|
||||||
result.append(this->operator[](i));
|
result.append(this->get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Command::Params::operator[](size_t index)
|
Command::ClientParams::ClientParams()
|
||||||
|
: nesting_(Game::cmd_args->nesting)
|
||||||
{
|
{
|
||||||
return this->get(index);
|
assert(Game::cmd_args->nesting < Game::CMD_MAX_NESTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Command::ClientParams::get(size_t index)
|
int Command::ClientParams::size()
|
||||||
{
|
{
|
||||||
if (index >= this->length()) return const_cast<char*>("");
|
return Game::cmd_args->argc[this->nesting_];
|
||||||
return Game::cmd_argv[this->commandId][index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Command::ClientParams::length()
|
const char* Command::ClientParams::get(const int index)
|
||||||
{
|
{
|
||||||
return Game::cmd_argc[this->commandId];
|
if (index >= this->size())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Command::ServerParams::get(size_t index)
|
return Game::cmd_args->argv[this->nesting_][index];
|
||||||
{
|
|
||||||
if (index >= this->length()) return const_cast<char*>("");
|
|
||||||
return Game::cmd_argv_sv[this->commandId][index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Command::ServerParams::length()
|
Command::ServerParams::ServerParams()
|
||||||
|
: nesting_(Game::sv_cmd_args->nesting)
|
||||||
{
|
{
|
||||||
return Game::cmd_argc_sv[this->commandId];
|
assert(Game::sv_cmd_args->nesting < Game::CMD_MAX_NESTING);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Command::ServerParams::size()
|
||||||
|
{
|
||||||
|
return Game::sv_cmd_args->argc[this->nesting_];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Command::ServerParams::get(const int index)
|
||||||
|
{
|
||||||
|
if (index >= this->size())
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return Game::sv_cmd_args->argv[this->nesting_][index];
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::Add(const char* name, Utils::Slot<Command::Callback> callback)
|
void Command::Add(const char* name, Utils::Slot<Command::Callback> callback)
|
||||||
{
|
{
|
||||||
std::string command = Utils::String::ToLower(name);
|
const auto command = Utils::String::ToLower(name);
|
||||||
|
|
||||||
if (Command::FunctionMap.find(command) == Command::FunctionMap.end())
|
if (Command::FunctionMap.find(command) == Command::FunctionMap.end())
|
||||||
{
|
{
|
||||||
Command::AddRaw(name, Command::MainCallback);
|
Command::AddRaw(name, Command::MainCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::FunctionMap[command] = callback;
|
Command::FunctionMap[command] = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::AddSV(const char* name, Utils::Slot<Command::Callback> callback)
|
void Command::AddSV(const char* name, Utils::Slot<Command::Callback> callback)
|
||||||
@ -70,7 +85,7 @@ namespace Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string command = Utils::String::ToLower(name);
|
const auto command = Utils::String::ToLower(name);
|
||||||
|
|
||||||
if (Command::FunctionMapSV.find(command) == Command::FunctionMapSV.end())
|
if (Command::FunctionMapSV.find(command) == Command::FunctionMapSV.end())
|
||||||
{
|
{
|
||||||
@ -80,7 +95,7 @@ namespace Components
|
|||||||
Command::AddRaw(name, Game::Cbuf_AddServerText);
|
Command::AddRaw(name, Game::Cbuf_AddServerText);
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::FunctionMapSV[command] = callback;
|
FunctionMapSV[command] = std::move(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::AddRaw(const char* name, void(*callback)(), bool key)
|
void Command::AddRaw(const char* name, void(*callback)(), bool key)
|
||||||
@ -134,25 +149,27 @@ namespace Components
|
|||||||
|
|
||||||
void Command::MainCallback()
|
void Command::MainCallback()
|
||||||
{
|
{
|
||||||
Command::ClientParams params(*Game::cmd_id);
|
Command::ClientParams params;
|
||||||
|
|
||||||
std::string command = Utils::String::ToLower(params[0]);
|
const auto command = Utils::String::ToLower(params[0]);
|
||||||
|
const auto got = Command::FunctionMap.find(command);
|
||||||
|
|
||||||
if (Command::FunctionMap.find(command) != Command::FunctionMap.end())
|
if (got != Command::FunctionMap.end())
|
||||||
{
|
{
|
||||||
Command::FunctionMap[command](¶ms);
|
got->second(¶ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command::MainCallbackSV()
|
void Command::MainCallbackSV()
|
||||||
{
|
{
|
||||||
Command::ServerParams params(*Game::cmd_id_sv);
|
Command::ServerParams params;
|
||||||
|
|
||||||
std::string command = Utils::String::ToLower(params[0]);
|
const auto command = Utils::String::ToLower(params[0]);
|
||||||
|
const auto got = Command::FunctionMapSV.find(command);
|
||||||
|
|
||||||
if (Command::FunctionMapSV.find(command) != Command::FunctionMapSV.end())
|
if (got != Command::FunctionMapSV.end())
|
||||||
{
|
{
|
||||||
Command::FunctionMapSV[command](¶ms);
|
got->second(¶ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,115 +177,12 @@ namespace Components
|
|||||||
{
|
{
|
||||||
AssertSize(Game::cmd_function_t, 24);
|
AssertSize(Game::cmd_function_t, 24);
|
||||||
|
|
||||||
static int toastDurationShort = 1000;
|
|
||||||
static int toastDurationMedium = 2500;
|
|
||||||
static int toastDurationLong = 5000;
|
|
||||||
|
|
||||||
// Disable native noclip command
|
|
||||||
Utils::Hook::Nop(0x474846, 5);
|
|
||||||
|
|
||||||
Command::Add("noclip", [](Command::Params*)
|
|
||||||
{
|
|
||||||
int clientNum = Game::CG_GetClientNum();
|
|
||||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
|
||||||
{
|
|
||||||
Logger::Print("You are not hosting a match!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
|
||||||
{
|
|
||||||
Logger::Print("Cheats disabled!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::g_entities[clientNum].client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
|
||||||
|
|
||||||
Logger::Print("Noclip toggled\n");
|
|
||||||
Toast::Show("cardicon_abduction", "Success", "Noclip toggled", toastDurationShort);
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("ufo", [](Command::Params*)
|
|
||||||
{
|
|
||||||
int clientNum = Game::CG_GetClientNum();
|
|
||||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
|
||||||
{
|
|
||||||
Logger::Print("You are not hosting a match!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
|
||||||
{
|
|
||||||
Logger::Print("Cheats disabled!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::g_entities[clientNum].client->flags ^= Game::PLAYER_FLAG_UFO;
|
|
||||||
|
|
||||||
Logger::Print("UFO toggled\n");
|
|
||||||
Toast::Show("cardicon_abduction", "Success", "UFO toggled", toastDurationShort);
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("setviewpos", [](Command::Params* params)
|
|
||||||
{
|
|
||||||
int clientNum = Game::CG_GetClientNum();
|
|
||||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
|
||||||
{
|
|
||||||
Logger::Print("You are not hosting a match!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
|
||||||
{
|
|
||||||
Logger::Print("Cheats disabled!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params->length() != 4 && params->length() != 6)
|
|
||||||
{
|
|
||||||
Logger::Print("Invalid coordinate specified!\n");
|
|
||||||
Toast::Show("cardicon_stop", "Error", "Invalid coordinate specified!", toastDurationMedium);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
float pos[3] = { 0.0f, 0.0f, 0.0f };
|
|
||||||
float orientation[3] = { 0.0f, 0.0f, 0.0f };
|
|
||||||
|
|
||||||
pos[0] = strtof(params->get(1), nullptr);
|
|
||||||
pos[1] = strtof(params->get(2), nullptr);
|
|
||||||
pos[2] = strtof(params->get(3), nullptr);
|
|
||||||
|
|
||||||
if (params->length() == 6)
|
|
||||||
{
|
|
||||||
orientation[0] = strtof(params->get(4), nullptr);
|
|
||||||
orientation[1] = strtof(params->get(5), nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::TeleportPlayer(&Game::g_entities[clientNum], pos, orientation);
|
|
||||||
|
|
||||||
// Logging that will spam the console and screen if people use cinematics
|
|
||||||
//Logger::Print("Successfully teleported player!\n");
|
|
||||||
//Toast::Show("cardicon_abduction", "Success", "You have been teleported!", toastDurationShort);
|
|
||||||
});
|
|
||||||
|
|
||||||
Command::Add("openLink", [](Command::Params* params)
|
Command::Add("openLink", [](Command::Params* params)
|
||||||
{
|
{
|
||||||
if (params->length() > 1)
|
if (params->size() > 1)
|
||||||
{
|
{
|
||||||
Utils::OpenUrl(params->get(1));
|
Utils::OpenUrl(params->get(1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::~Command()
|
|
||||||
{
|
|
||||||
Command::FunctionMap.clear();
|
|
||||||
Command::FunctionMapSV.clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,46 +9,44 @@ namespace Components
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Params() {};
|
Params() {};
|
||||||
virtual ~Params() {};
|
|
||||||
virtual char* get(size_t index) = 0;
|
|
||||||
virtual size_t length() = 0;
|
|
||||||
|
|
||||||
virtual std::string join(size_t startIndex);
|
virtual int size() = 0;
|
||||||
virtual char* operator[](size_t index);
|
virtual const char* get(int index) = 0;
|
||||||
|
virtual std::string join(int index);
|
||||||
|
|
||||||
|
virtual const char* operator[](const int index)
|
||||||
|
{
|
||||||
|
return this->get(index);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientParams : public Params
|
class ClientParams : public Params
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ClientParams(unsigned int id) : commandId(id) {};
|
ClientParams();
|
||||||
ClientParams(const ClientParams &obj) : commandId(obj.commandId) {};
|
|
||||||
ClientParams() : ClientParams(*Game::cmd_id) {};
|
|
||||||
|
|
||||||
char* get(size_t index) override;
|
int size() override;
|
||||||
size_t length() override;
|
const char* get(int index) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int commandId;
|
int nesting_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ServerParams : public Params
|
class ServerParams : public Params
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ServerParams(unsigned int id) : commandId(id) {};
|
ServerParams();
|
||||||
ServerParams(const ServerParams &obj) : commandId(obj.commandId) {};
|
|
||||||
ServerParams() : ServerParams(*Game::cmd_id_sv) {};
|
|
||||||
|
|
||||||
char* get(size_t index) override;
|
int size() override;
|
||||||
size_t length() override;
|
const char* get(int index) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int commandId;
|
int nesting_;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void(Callback)(Command::Params* params);
|
typedef void(Callback)(Command::Params* params);
|
||||||
|
|
||||||
Command();
|
Command();
|
||||||
~Command();
|
|
||||||
|
|
||||||
static Game::cmd_function_t* Allocate();
|
static Game::cmd_function_t* Allocate();
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "STDInclude.hpp"
|
#include <STDInclude.hpp>
|
||||||
|
|
||||||
namespace Components
|
namespace Components
|
||||||
{
|
{
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user