mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-10 15:20:48 -05:00
Merge branch 'develop' into release/pre
This commit is contained in:
257
.github/workflows/build_application.yml
vendored
Normal file
257
.github/workflows/build_application.yml
vendored
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
name: Application build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop, release/pre, master ]
|
||||||
|
paths:
|
||||||
|
- Application/**
|
||||||
|
- WebfrontCore/**
|
||||||
|
- Data/**
|
||||||
|
- SharedLibraryCore/**
|
||||||
|
- Plugins/**
|
||||||
|
pull_request:
|
||||||
|
branches: [ develop ]
|
||||||
|
paths:
|
||||||
|
- Application/**
|
||||||
|
- WebfrontCore/**
|
||||||
|
- Data/**
|
||||||
|
- SharedLibraryCore/**
|
||||||
|
- Plugins/**
|
||||||
|
|
||||||
|
env:
|
||||||
|
releaseType: prerelease
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_revision_number:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
revision_number: ${{ steps.revision.outputs.revision_number }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore cache
|
||||||
|
id: cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: cache_dir
|
||||||
|
key: revision-number
|
||||||
|
|
||||||
|
- name: Get current date
|
||||||
|
id: date
|
||||||
|
run: echo "current_date=$(date +'%Y-%m-%d')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Check and update revision number
|
||||||
|
id: revision
|
||||||
|
run: |
|
||||||
|
FILENAME=cache_dir/revision_number.txt
|
||||||
|
DATEFILE=cache_dir/previous_date.txt
|
||||||
|
|
||||||
|
mkdir -p cache_dir
|
||||||
|
|
||||||
|
if [ -f "$DATEFILE" ]; then
|
||||||
|
prev_date=$(cat "$DATEFILE")
|
||||||
|
rev_number=$(cat "$FILENAME")
|
||||||
|
else
|
||||||
|
prev_date=""
|
||||||
|
rev_number=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$current_date" = "$prev_date" ]; then
|
||||||
|
rev_number=$((rev_number + 1))
|
||||||
|
else
|
||||||
|
rev_number=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "New revision number: $rev_number"
|
||||||
|
echo $rev_number > "$FILENAME"
|
||||||
|
echo $current_date > "$DATEFILE"
|
||||||
|
echo "revision_number=$rev_number" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Save cache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: cache_dir
|
||||||
|
key: revision-number
|
||||||
|
|
||||||
|
make_version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ update_revision_number ]
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
build_num: ${{ steps.generate_build_number.outputs.build_num }}
|
||||||
|
env:
|
||||||
|
revisionNumber: ${{ needs.update_revision_number.outputs.revision_number }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Make build number
|
||||||
|
id: generate_build_number
|
||||||
|
run: |
|
||||||
|
build_num=$(date +'%Y.%-m.%-d').${{ env.revisionNumber }}
|
||||||
|
echo "build_num=$build_num" >> $GITHUB_OUTPUT
|
||||||
|
echo "Build number is $build_num"
|
||||||
|
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ make_version ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
solution: IW4MAdmin.sln
|
||||||
|
buildConfiguration: Prerelease
|
||||||
|
isPreRelease: false
|
||||||
|
|
||||||
|
buildPlatform: Any CPU
|
||||||
|
outputFolder: ${{ github.workspace }}/Publish/Prerelease
|
||||||
|
buildNumber: ${{ needs.make_version.outputs.build_num }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup .NET SDK
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Restore NuGet packages
|
||||||
|
run: dotnet restore ${{ env.solution }}
|
||||||
|
|
||||||
|
- name: Preload external resources
|
||||||
|
run: |
|
||||||
|
echo "Build Configuration is ${{ env.buildConfiguration }}, Release Type is ${{ env.releaseType }}"
|
||||||
|
mkdir -p WebfrontCore/wwwroot/lib/open-iconic/font/css
|
||||||
|
curl -o WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss
|
||||||
|
sed -i 's#../fonts/#/font/#g' WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss
|
||||||
|
|
||||||
|
- name: Build webfront
|
||||||
|
run: dotnet build WebfrontCore/WebfrontCore.csproj -c ${{ env.buildConfiguration }} /p:Configuration=${{ env.buildConfiguration }} /p:Platform="x64"
|
||||||
|
|
||||||
|
- name: Compile SCSS files
|
||||||
|
run: |
|
||||||
|
dotnet tool install Excubo.WebCompiler --global
|
||||||
|
webcompiler -r WebfrontCore/wwwroot/css/src -o WebfrontCore/wwwroot/css/ -m disable -z disable
|
||||||
|
webcompiler WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss -o WebfrontCore/wwwroot/css/ -m disable -z disable
|
||||||
|
|
||||||
|
- name: Bundle JS files
|
||||||
|
run: |
|
||||||
|
echo 'Getting dotnet bundle'
|
||||||
|
curl -o ${{ github.workspace }}/dotnet-bundle.zip https://raidmax.org/IW4MAdmin/res/dotnet-bundle.zip
|
||||||
|
echo 'Unzipping download'
|
||||||
|
unzip ${{ github.workspace }}/dotnet-bundle.zip -d ${{ github.workspace }}/bundle
|
||||||
|
echo 'Executing dotnet-bundle'
|
||||||
|
cd ${{ github.workspace }}/bundle
|
||||||
|
dotnet dotnet-bundle.dll clean ${{ github.workspace }}/WebfrontCore/bundleconfig.json
|
||||||
|
dotnet dotnet-bundle.dll ${{ github.workspace }}/WebfrontCore/bundleconfig.json
|
||||||
|
|
||||||
|
- name: Build plugins
|
||||||
|
run: |
|
||||||
|
cd Plugins
|
||||||
|
find . -name "*.csproj" -print0 | xargs -0 -I {} dotnet publish {} -c ${{ env.buildConfiguration }} -o ../BUILD/Plugins /p:Configuration=${{ env.buildConfiguration }} /p:Platform="x64" /p:DeployOnBuild=false /p:PackageAsSingleFile=false /p:SkipInvalidConfigurations=true /p:Version=${{ env.buildNumber }} --no-restore
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: dotnet publish Application/Application.csproj -c ${{ env.buildConfiguration }} -o ${{ env.outputFolder }} /p:Version=${{ env.buildNumber }} /p:Configuration=${{ env.buildConfiguration }} /p:Platform="x64" --no-restore
|
||||||
|
|
||||||
|
- name: Download translations
|
||||||
|
run: |
|
||||||
|
mkdir -p "${{ env.outputFolder }}/Localization"
|
||||||
|
localizations=("en-US" "ru-RU" "es-EC" "pt-BR" "de-DE")
|
||||||
|
for localization in "${localizations[@]}"
|
||||||
|
do
|
||||||
|
url="https://master.iw4.zip/localization/$localization"
|
||||||
|
filePath="${{ env.outputFolder }}/Localization/IW4MAdmin.$localization.json"
|
||||||
|
curl -s "$url" -o "$filePath"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Clean up publish files
|
||||||
|
run: |
|
||||||
|
chmod +x ${{ github.workspace }}/Application/BuildScripts/PostBuild.sh
|
||||||
|
bash ${{ github.workspace }}/Application/BuildScripts/PostBuild.sh ${{ env.outputFolder }} ${{ github.workspace }}
|
||||||
|
|
||||||
|
- name: Generate start scripts
|
||||||
|
run: |
|
||||||
|
cat << EOF > "${{ env.outputFolder }}/StartIW4MAdmin.cmd"
|
||||||
|
@echo off
|
||||||
|
@title IW4MAdmin
|
||||||
|
set DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
dotnet Lib\IW4MAdmin.dll
|
||||||
|
pause
|
||||||
|
EOF
|
||||||
|
|
||||||
|
cat << EOF > "${{ env.outputFolder }}/StartIW4MAdmin.sh"
|
||||||
|
#!/bin/bash
|
||||||
|
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
dotnet Lib/IW4MAdmin.dll
|
||||||
|
EOF
|
||||||
|
|
||||||
|
- name: Move extra content into publish directory
|
||||||
|
run: |
|
||||||
|
cp ${{ github.workspace }}/Plugins/ScriptPlugins/*.js ${{ env.outputFolder }}/Plugins/
|
||||||
|
cp ${{ github.workspace }}/BUILD/Plugins/*.dll ${{ env.outputFolder }}/Plugins/
|
||||||
|
mkdir -p ${{ env.outputFolder }}/wwwroot/css
|
||||||
|
cp ${{ github.workspace }}/WebfrontCore/wwwroot/css/global.min.css ${{ env.outputFolder }}/wwwroot/css/global.min.css
|
||||||
|
mkdir -p ${{ env.outputFolder }}/wwwroot/js
|
||||||
|
cp ${{ github.workspace }}/WebfrontCore/wwwroot/js/global.min.js ${{ env.outputFolder }}/wwwroot/js/global.min.js
|
||||||
|
mkdir -p ${{ env.outputFolder }}/wwwroot/font
|
||||||
|
rsync -ar ${{ github.workspace }}/WebfrontCore/wwwroot/lib/open-iconic/font/fonts/ ${{ env.outputFolder }}/wwwroot/font
|
||||||
|
mkdir -p ${{ env.outputFolder }}/GameFiles
|
||||||
|
rsync -ar ${{ github.workspace }}/GameFiles/ ${{ env.outputFolder }}/GameFiles
|
||||||
|
mkdir -p ${{ env.outputFolder }}/wwwroot/images/
|
||||||
|
rsync -ar ${{ github.workspace }}/WebfrontCore/wwwroot/images/ ${{ env.outputFolder }}/wwwroot/images/
|
||||||
|
rsync -ar ${{ github.workspace }}/BUILD/Plugins/wwwroot/ ${{ env.outputFolder }}/wwwroot/
|
||||||
|
|
||||||
|
- name: Upload artifact for analysis
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: IW4MAdmin-${{ env.buildNumber }}-${{ env.releaseType }}
|
||||||
|
path: ${{ env.outputFolder }}
|
||||||
|
|
||||||
|
release_github:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ make_version, build ]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
environment: prerelease
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release/pre' }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
buildNumber: ${{ needs.make_version.outputs.build_num }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download build
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: IW4MAdmin-${{ env.buildNumber }}-${{ env.releaseType }}
|
||||||
|
path: ${{ github.workspace }}
|
||||||
|
|
||||||
|
- name: Zip build
|
||||||
|
run: zip -r IW4MAdmin-${{ env.buildNumber }}.zip ${{ github.workspace }}/*
|
||||||
|
|
||||||
|
- name: Make release
|
||||||
|
uses: ncipollo/release-action@v1
|
||||||
|
with:
|
||||||
|
tag: ${{ env.buildNumber }}-${{ env.releaseType }}
|
||||||
|
name: IW4MAdmin ${{ env.buildNumber }}
|
||||||
|
draft: false
|
||||||
|
prerelease: true
|
||||||
|
body: Automated rolling release - changelog below. [Updating Instructions](https://github.com/RaidMax/IW4M-Admin/wiki/Getting-Started#updating)
|
||||||
|
generateReleaseNotes: true
|
||||||
|
artifacts: ${{ github.workspace }}/*.zip
|
||||||
|
artifactErrorsFailBuild: true
|
||||||
|
|
||||||
|
update_master_version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ make_version, build, release_github ]
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release/pre' }}
|
||||||
|
|
||||||
|
env:
|
||||||
|
buildNumber: ${{ needs.make_version.outputs.build_num }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Update master version
|
||||||
|
run: |
|
||||||
|
curl --header "Content-Type: application/json" \
|
||||||
|
--request POST \
|
||||||
|
--data '{"current-version-${{ env.releaseType }}":"${{ env.buildNumber }}","jwt-secret": "${{ secrets.JWTSecret }}"}' \
|
||||||
|
http://api.raidmax.org:5000/version
|
93
.github/workflows/shared_library_nuget.yml
vendored
Normal file
93
.github/workflows/shared_library_nuget.yml
vendored
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
name: SharedLibraryCore NuGet
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ develop, release/pre, master ]
|
||||||
|
paths:
|
||||||
|
- SharedLibraryCore/**
|
||||||
|
- Data/**
|
||||||
|
- .github/workflows/shared_library_nuget.yml
|
||||||
|
pull_request:
|
||||||
|
branches: [ develop ]
|
||||||
|
paths:
|
||||||
|
- SharedLibraryCore/**
|
||||||
|
- Data/**
|
||||||
|
|
||||||
|
env:
|
||||||
|
outputDirectory: ${{ github.workspace}}/nuget
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
make_version:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
build_num: ${{ steps.generate_build_number.outputs.build_num }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Make build number
|
||||||
|
id: generate_build_number
|
||||||
|
run: |
|
||||||
|
run_number=$(git log --since=$(date +'%Y-%m-%dT00:00:00') --oneline | grep -c 'workflow_run')
|
||||||
|
run_number=$((run_number + 1))
|
||||||
|
build_num=$(date +'%Y.%-m.%-d').$(run_number)
|
||||||
|
echo "build_num=$build_num" >> $GITHUB_OUTPUT
|
||||||
|
echo "Build number is $build_num"
|
||||||
|
|
||||||
|
build_pack:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [ make_version ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
buildNumber: ${{ needs.make_version.outputs.build_num }}
|
||||||
|
packageTag: ${{ github.event_name == 'pull_request' && '-beta' || '-preview' }}
|
||||||
|
buildConfiguration: Prerelease
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build data
|
||||||
|
run: dotnet build **/Data.csproj -c ${{env.buildConfiguration}} /p:Version=${{ env.buildNumber }} --no-restore
|
||||||
|
|
||||||
|
- name: Build SLC
|
||||||
|
run: dotnet build **/SharedLibraryCore.csproj -c ${{env.buildConfiguration}} /p:Version=${{ env.buildNumber }} --no-restore
|
||||||
|
|
||||||
|
- name: Pack SLC
|
||||||
|
run: dotnet pack **/SharedLibraryCore.csproj -c ${{env.buildConfiguration}} -p:PackageVersion=${{ env.buildNumber }}${{ env.packageTag }} -o ${{ env.outputDirectory }} --no-restore
|
||||||
|
|
||||||
|
- name: Publish nuget package artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SharedLibraryCore-${{ env.buildNumber }}
|
||||||
|
path: ${{ env.outputDirectory }}/*.nupkg
|
||||||
|
|
||||||
|
publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
needs: [ make_version, build_pack ]
|
||||||
|
environment: prerelease
|
||||||
|
if: ${{ github.ref == 'refs/heads/master' || github.ref == 'refs/heads/release/pre' || github.ref == 'refs/heads/develop' }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Download artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: SharedLibraryCore-${{ needs.make_version.outputs.build_num }}
|
||||||
|
path: ${{ env.outputDirectory }}
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Publish NuGet package
|
||||||
|
run: |
|
||||||
|
for file in ${{ env.outputDirectory }}/*.nupkg; do
|
||||||
|
dotnet nuget push "$file" --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
|
||||||
|
done
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -247,3 +247,4 @@ launchSettings.json
|
|||||||
*.db
|
*.db
|
||||||
/Data/IW4MAdmin_Migration.db-shm
|
/Data/IW4MAdmin_Migration.db-shm
|
||||||
/Data/IW4MAdmin_Migration.db-wal
|
/Data/IW4MAdmin_Migration.db-wal
|
||||||
|
bundle/
|
@ -1,12 +1,12 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using RestEase;
|
using Refit;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API.GameLogServer
|
namespace IW4MAdmin.Application.API.GameLogServer
|
||||||
{
|
{
|
||||||
[Header("User-Agent", "IW4MAdmin-RestEase")]
|
[Headers("User-Agent: IW4MAdmin-RestEase")]
|
||||||
public interface IGameLogServer
|
public interface IGameLogServer
|
||||||
{
|
{
|
||||||
[Get("log/{path}/{key}")]
|
[Get("/log/{path}/{key}")]
|
||||||
Task<LogInfo> Log([Path] string path, [Path] string key);
|
Task<LogInfo> Log(string path, string key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,16 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API.GameLogServer
|
namespace IW4MAdmin.Application.API.GameLogServer
|
||||||
{
|
{
|
||||||
public class LogInfo
|
public class LogInfo
|
||||||
{
|
{
|
||||||
[JsonProperty("success")]
|
[JsonPropertyName("success")]
|
||||||
public bool Success { get; set; }
|
public bool Success { get; set; }
|
||||||
[JsonProperty("length")]
|
[JsonPropertyName("length")]
|
||||||
public int Length { get; set; }
|
public int Length { get; set; }
|
||||||
[JsonProperty("data")]
|
[JsonPropertyName("data")]
|
||||||
public string Data { get; set; }
|
public string Data { get; set; }
|
||||||
[JsonProperty("next_key")]
|
[JsonPropertyName("next_key")]
|
||||||
public string NextKey { get; set; }
|
public string NextKey { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API.Master
|
namespace IW4MAdmin.Application.API.Master
|
||||||
@ -12,32 +12,32 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unique ID of the instance
|
/// Unique ID of the instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates how long the instance has been running
|
/// Indicates how long the instance has been running
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("uptime")]
|
[JsonPropertyName("uptime")]
|
||||||
public int Uptime { get; set; }
|
public int Uptime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the version of the instance
|
/// Specifies the version of the instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("version")]
|
[JsonPropertyName("version")]
|
||||||
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
||||||
public BuildNumber Version { get; set; }
|
public BuildNumber Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List of servers the instance is monitoring
|
/// List of servers the instance is monitoring
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("servers")]
|
[JsonPropertyName("servers")]
|
||||||
public List<ApiServer> Servers { get; set; }
|
public List<ApiServer> Servers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Url IW4MAdmin is listening on
|
/// Url IW4MAdmin is listening on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty("webfront_url")]
|
[JsonPropertyName("webfront_url")]
|
||||||
public string WebfrontUrl { get; set; }
|
public string WebfrontUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API.Master
|
namespace IW4MAdmin.Application.API.Master
|
||||||
{
|
{
|
||||||
public class ApiServer
|
public class ApiServer
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")]
|
||||||
public long Id { get; set; }
|
public long Id { get; set; }
|
||||||
[JsonProperty("ip")]
|
[JsonPropertyName("ip")]
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
[JsonProperty("port")]
|
[JsonPropertyName("port")]
|
||||||
public short Port { get; set; }
|
public short Port { get; set; }
|
||||||
[JsonProperty("version")]
|
[JsonPropertyName("version")]
|
||||||
public string Version { get; set; }
|
public string Version { get; set; }
|
||||||
[JsonProperty("gametype")]
|
[JsonPropertyName("gametype")]
|
||||||
public string Gametype { get; set; }
|
public string Gametype { get; set; }
|
||||||
[JsonProperty("map")]
|
[JsonPropertyName("map")]
|
||||||
public string Map { get; set; }
|
public string Map { get; set; }
|
||||||
[JsonProperty("game")]
|
[JsonPropertyName("game")]
|
||||||
public string Game { get; set; }
|
public string Game { get; set; }
|
||||||
[JsonProperty("hostname")]
|
[JsonPropertyName("hostname")]
|
||||||
public string Hostname { get; set; }
|
public string Hostname { get; set; }
|
||||||
[JsonProperty("clientnum")]
|
[JsonPropertyName("clientnum")]
|
||||||
public int ClientNum { get; set; }
|
public int ClientNum { get; set; }
|
||||||
[JsonProperty("maxclientnum")]
|
[JsonPropertyName("maxclientnum")]
|
||||||
public int MaxClientNum { get; set; }
|
public int MaxClientNum { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,37 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using IW4MAdmin.Application.Plugin;
|
using IW4MAdmin.Application.Plugin;
|
||||||
using Newtonsoft.Json;
|
using Refit;
|
||||||
using RestEase;
|
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.API.Master
|
namespace IW4MAdmin.Application.API.Master;
|
||||||
{
|
|
||||||
public class AuthenticationId
|
public class AuthenticationId
|
||||||
{
|
{
|
||||||
[JsonProperty("id")]
|
[JsonPropertyName("id")] public string Id { get; set; }
|
||||||
public string Id { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TokenId
|
public class TokenId
|
||||||
{
|
{
|
||||||
[JsonProperty("access_token")]
|
[JsonPropertyName("access_token")] public string AccessToken { get; set; }
|
||||||
public string AccessToken { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VersionInfo
|
public class VersionInfo
|
||||||
{
|
{
|
||||||
[JsonProperty("current-version-stable")]
|
[JsonPropertyName("current-version-stable")]
|
||||||
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
||||||
public BuildNumber CurrentVersionStable { get; set; }
|
public BuildNumber CurrentVersionStable { get; set; }
|
||||||
|
|
||||||
[JsonProperty("current-version-prerelease")]
|
[JsonPropertyName("current-version-prerelease")]
|
||||||
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
[JsonConverter(typeof(BuildNumberJsonConverter))]
|
||||||
public BuildNumber CurrentVersionPrerelease { get; set; }
|
public BuildNumber CurrentVersionPrerelease { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ResultMessage
|
public class ResultMessage
|
||||||
{
|
{
|
||||||
[JsonProperty("message")]
|
[JsonPropertyName("message")] public string Message { get; set; }
|
||||||
public string Message { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PluginSubscriptionContent
|
public class PluginSubscriptionContent
|
||||||
@ -43,37 +40,31 @@ namespace IW4MAdmin.Application.API.Master
|
|||||||
public PluginType Type { get; set; }
|
public PluginType Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the capabilities of the master API
|
/// Defines the capabilities of the master API
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Header("User-Agent", "IW4MAdmin-RestEase")]
|
[Headers("User-Agent: IW4MAdmin-RestEase")]
|
||||||
public interface IMasterApi
|
public interface IMasterApi
|
||||||
{
|
{
|
||||||
[Header("Authorization")]
|
[Post("/authenticate")]
|
||||||
string AuthorizationToken { get; set; }
|
|
||||||
|
|
||||||
[Post("authenticate")]
|
|
||||||
Task<TokenId> Authenticate([Body] AuthenticationId Id);
|
Task<TokenId> Authenticate([Body] AuthenticationId Id);
|
||||||
|
|
||||||
[Post("instance/")]
|
[Post("/instance/")]
|
||||||
[AllowAnyStatusCode]
|
Task<IApiResponse<ResultMessage>> AddInstance([Body] ApiInstance instance, [Header("Authorization")] string authorization);
|
||||||
Task<Response<ResultMessage>> AddInstance([Body] ApiInstance instance);
|
|
||||||
|
|
||||||
[Put("instance/{id}")]
|
[Put("/instance/{id}")]
|
||||||
[AllowAnyStatusCode]
|
Task<IApiResponse<ResultMessage>> UpdateInstance(string id, [Body] ApiInstance instance, [Header("Authorization")] string authorization);
|
||||||
Task<Response<ResultMessage>> UpdateInstance([Path] string id, [Body] ApiInstance instance);
|
|
||||||
|
|
||||||
[Get("version/{apiVersion}")]
|
[Get("/version/{apiVersion}")]
|
||||||
Task<VersionInfo> GetVersion([Path] int apiVersion);
|
Task<VersionInfo> GetVersion(int apiVersion);
|
||||||
|
|
||||||
[Get("localization")]
|
[Get("/localization")]
|
||||||
Task<List<SharedLibraryCore.Localization.Layout>> GetLocalization();
|
Task<List<SharedLibraryCore.Localization.Layout>> GetLocalization();
|
||||||
|
|
||||||
[Get("localization/{languageTag}")]
|
[Get("/localization/{languageTag}")]
|
||||||
Task<SharedLibraryCore.Localization.Layout> GetLocalization([Path("languageTag")] string languageTag);
|
Task<SharedLibraryCore.Localization.Layout> GetLocalization(string languageTag);
|
||||||
|
|
||||||
[Get("plugin_subscriptions")]
|
[Get("/plugin_subscriptions")]
|
||||||
Task<IEnumerable<PluginSubscriptionContent>> GetPluginSubscription([Query("instance_id")] Guid instanceId, [Query("subscription_id")] string subscription_id);
|
Task<IEnumerable<PluginSubscriptionContent>> GetPluginSubscription([Query] string instance_id,
|
||||||
}
|
[Query] string subscription_id);
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
|
||||||
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
|
||||||
<Version>2020.0.0.0</Version>
|
<Version>2020.0.0.0</Version>
|
||||||
@ -21,20 +21,21 @@
|
|||||||
<Win32Resource />
|
<Win32Resource />
|
||||||
<RootNamespace>IW4MAdmin.Application</RootNamespace>
|
<RootNamespace>IW4MAdmin.Application</RootNamespace>
|
||||||
<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
|
<PublishWithAspNetCoreTargetManifest>false</PublishWithAspNetCoreTargetManifest>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Jint" Version="3.0.0-beta-2049" />
|
<PackageReference Include="Jint" Version="3.1.3" />
|
||||||
<PackageReference Include="MaxMind.GeoIP2" Version="5.1.0" />
|
<PackageReference Include="MaxMind.GeoIP2" Version="5.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
<PackageReference Include="RestEase" Version="1.5.7" />
|
<PackageReference Include="Refit" Version="7.1.0" />
|
||||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
|
||||||
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.4.0-alpha.22272.1" />
|
<PackageReference Include="System.CommandLine.DragonFruit" Version="0.4.0-alpha.22272.1" />
|
||||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="6.0.0" />
|
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@ -72,18 +73,8 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
<Target Name="PreBuild" BeforeTargets="PreBuildEvent" Condition="'$(ConfigurationName)'=='Debug'">
|
||||||
<Exec Command="if $(ConfigurationName) == Debug call $(ProjectDir)BuildScripts\PreBuild.bat $(ProjectDir)..\ $(ProjectDir) $(TargetDir) $(OutDir)" />
|
<Exec Command="powershell.exe $(ProjectDir)BuildScripts\PreBuild.ps1 $(SolutionDir) $(TargetDir)" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
|
|
||||||
<Output TaskParameter="Assemblies" ItemName="CurrentAssembly" />
|
|
||||||
</GetAssemblyIdentity>
|
|
||||||
<Exec Command="if $(ConfigurationName) == Debug call $(ProjectDir)BuildScripts\PostBuild.bat $(ProjectDir)..\ $(ProjectDir) $(TargetDir) $(OutDir) %25(CurrentAssembly.Version)" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<Target Name="PostPublish" AfterTargets="Publish">
|
|
||||||
<Exec Command="if $(ConfigurationName) == Debug call $(ProjectDir)BuildScripts\PostPublish.bat $(ProjectDir)..\ $(ProjectDir) $(TargetDir) $(ConfigurationName)" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -26,6 +26,7 @@ using Data.Abstractions;
|
|||||||
using Data.Context;
|
using Data.Context;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using IW4MAdmin.Application.Configuration;
|
using IW4MAdmin.Application.Configuration;
|
||||||
|
using IW4MAdmin.Application.IO;
|
||||||
using IW4MAdmin.Application.Migration;
|
using IW4MAdmin.Application.Migration;
|
||||||
using IW4MAdmin.Application.Plugin.Script;
|
using IW4MAdmin.Application.Plugin.Script;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@ -68,6 +69,7 @@ namespace IW4MAdmin.Application
|
|||||||
private readonly ClientService ClientSvc;
|
private readonly ClientService ClientSvc;
|
||||||
readonly PenaltyService PenaltySvc;
|
readonly PenaltyService PenaltySvc;
|
||||||
private readonly IAlertManager _alertManager;
|
private readonly IAlertManager _alertManager;
|
||||||
|
private readonly ConfigurationWatcher _watcher;
|
||||||
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
|
||||||
readonly IPageList PageList;
|
readonly IPageList PageList;
|
||||||
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
|
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
|
||||||
@ -94,7 +96,8 @@ namespace IW4MAdmin.Application
|
|||||||
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
IEnumerable<IPlugin> plugins, IParserRegexFactory parserRegexFactory, IEnumerable<IRegisterEvent> customParserEvents,
|
||||||
ICoreEventHandler coreEventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory,
|
ICoreEventHandler coreEventHandler, IScriptCommandFactory scriptCommandFactory, IDatabaseContextFactory contextFactory,
|
||||||
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
|
IMetaRegistration metaRegistration, IScriptPluginServiceResolver scriptPluginServiceResolver, ClientService clientService, IServiceProvider serviceProvider,
|
||||||
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager, IInteractionRegistration interactionRegistration, IEnumerable<IPluginV2> v2PLugins)
|
ChangeHistoryService changeHistoryService, ApplicationConfiguration appConfig, PenaltyService penaltyService, IAlertManager alertManager, IInteractionRegistration interactionRegistration, IEnumerable<IPluginV2> v2PLugins,
|
||||||
|
ConfigurationWatcher watcher)
|
||||||
{
|
{
|
||||||
MiddlewareActionHandler = actionHandler;
|
MiddlewareActionHandler = actionHandler;
|
||||||
_servers = new ConcurrentBag<Server>();
|
_servers = new ConcurrentBag<Server>();
|
||||||
@ -102,10 +105,11 @@ namespace IW4MAdmin.Application
|
|||||||
ClientSvc = clientService;
|
ClientSvc = clientService;
|
||||||
PenaltySvc = penaltyService;
|
PenaltySvc = penaltyService;
|
||||||
_alertManager = alertManager;
|
_alertManager = alertManager;
|
||||||
|
_watcher = watcher;
|
||||||
ConfigHandler = appConfigHandler;
|
ConfigHandler = appConfigHandler;
|
||||||
StartTime = DateTime.UtcNow;
|
StartTime = DateTime.UtcNow;
|
||||||
PageList = new PageList();
|
PageList = new PageList();
|
||||||
AdditionalEventParsers = new List<IEventParser> { new BaseEventParser(parserRegexFactory, logger, _appConfig) };
|
AdditionalEventParsers = new List<IEventParser> { new BaseEventParser(parserRegexFactory, logger, _appConfig, serviceProvider.GetRequiredService<IGameScriptEventFactory>()) };
|
||||||
AdditionalRConParsers = new List<IRConParser> { new BaseRConParser(serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), parserRegexFactory) };
|
AdditionalRConParsers = new List<IRConParser> { new BaseRConParser(serviceProvider.GetRequiredService<ILogger<BaseRConParser>>(), parserRegexFactory) };
|
||||||
TokenAuthenticator = new TokenAuthentication();
|
TokenAuthenticator = new TokenAuthentication();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -529,6 +533,7 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
Console.WriteLine(_translationLookup["MANAGER_COMMUNICATION_INFO"]);
|
Console.WriteLine(_translationLookup["MANAGER_COMMUNICATION_INFO"]);
|
||||||
await InitializeServers();
|
await InitializeServers();
|
||||||
|
_watcher.Enable();
|
||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -710,7 +715,7 @@ namespace IW4MAdmin.Application
|
|||||||
|
|
||||||
public IEventParser GenerateDynamicEventParser(string name)
|
public IEventParser GenerateDynamicEventParser(string name)
|
||||||
{
|
{
|
||||||
return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration())
|
return new DynamicEventParser(_parserRegexFactory, _logger, ConfigHandler.Configuration(), _serviceProvider.GetRequiredService<IGameScriptEventFactory>())
|
||||||
{
|
{
|
||||||
Name = name
|
Name = name
|
||||||
};
|
};
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
param (
|
|
||||||
[string]$OutputDir = $(throw "-OutputDir is required.")
|
|
||||||
)
|
|
||||||
|
|
||||||
$localizations = @("en-US", "ru-RU", "es-EC", "pt-BR", "de-DE")
|
|
||||||
foreach($localization in $localizations)
|
|
||||||
{
|
|
||||||
$url = "http://api.raidmax.org:5000/localization/{0}" -f $localization
|
|
||||||
$filePath = "{0}Localization\IW4MAdmin.{1}.json" -f $OutputDir, $localization
|
|
||||||
$response = Invoke-WebRequest $url -UseBasicParsing
|
|
||||||
Out-File -FilePath $filePath -InputObject $response.Content -Encoding utf8
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
set SolutionDir=%1
|
|
||||||
set ProjectDir=%2
|
|
||||||
set TargetDir=%3
|
|
||||||
set OutDir=%4
|
|
||||||
set Version=%5
|
|
||||||
|
|
||||||
echo Copying dependency configs
|
|
||||||
copy "%SolutionDir%WebfrontCore\%OutDir%*.deps.json" "%TargetDir%"
|
|
||||||
copy "%SolutionDir%SharedLibraryCore\%OutDir%*.deps.json" "%TargetDir%"
|
|
||||||
|
|
||||||
if not exist "%TargetDir%Plugins" (
|
|
||||||
echo "Making plugin dir"
|
|
||||||
md "%TargetDir%Plugins"
|
|
||||||
)
|
|
||||||
|
|
||||||
xcopy /y "%SolutionDir%Build\Plugins" "%TargetDir%Plugins\"
|
|
||||||
del "%TargetDir%Plugins\SQLite*"
|
|
50
Application/BuildScripts/PostBuild.sh
Normal file
50
Application/BuildScripts/PostBuild.sh
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
PublishDir="$1"
|
||||||
|
SourceDir="$2"
|
||||||
|
|
||||||
|
if [ -z "$PublishDir" ] || [ -z "$SourceDir" ]; then
|
||||||
|
echo "Usage: $0 <PublishDir> <SourceDir>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Deleting extra runtime files"
|
||||||
|
declare -a runtimes=("linux-arm" "linux-arm64" "linux-armel" "osx" "osx-x64" "win-arm" "win-arm64" "alpine-x64" "linux-musl-x64")
|
||||||
|
for runtime in "${runtimes[@]}"; do
|
||||||
|
if [ -d "$PublishDir/runtimes/$runtime" ]; then
|
||||||
|
rm -rf "$PublishDir/runtimes/$runtime"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Deleting misc files"
|
||||||
|
if [ -f "$PublishDir/web.config" ]; then rm "$PublishDir/web.config"; fi
|
||||||
|
if [ -f "$PublishDir/libman.json" ]; then rm "$PublishDir/libman.json"; fi
|
||||||
|
rm -f "$PublishDir"/*.exe
|
||||||
|
rm -f "$PublishDir"/*.pdb
|
||||||
|
rm -f "$PublishDir"/IW4MAdmin
|
||||||
|
|
||||||
|
echo "Setting up default folders"
|
||||||
|
mkdir -p "$PublishDir/Plugins"
|
||||||
|
mkdir -p "$PublishDir/Configuration"
|
||||||
|
mv "$PublishDir/DefaultSettings.json" "$PublishDir/Configuration/"
|
||||||
|
|
||||||
|
mkdir -p "$PublishDir/Lib"
|
||||||
|
rm -f "$PublishDir/Microsoft.CodeAnalysis*.dll"
|
||||||
|
mv "$PublishDir"/*.dll "$PublishDir/Lib/"
|
||||||
|
mv "$PublishDir"/*.json "$PublishDir/Lib/"
|
||||||
|
mv "$PublishDir/runtimes" "$PublishDir/Lib/runtimes"
|
||||||
|
mv "$PublishDir/ru" "$PublishDir/Lib/ru"
|
||||||
|
mv "$PublishDir/de" "$PublishDir/Lib/de"
|
||||||
|
mv "$PublishDir/pt" "$PublishDir/Lib/pt"
|
||||||
|
mv "$PublishDir/es" "$PublishDir/Lib/es"
|
||||||
|
rm -rf "$PublishDir/cs"
|
||||||
|
rm -rf "$PublishDir/fr"
|
||||||
|
rm -rf "$PublishDir/it"
|
||||||
|
rm -rf "$PublishDir/ja"
|
||||||
|
rm -rf "$PublishDir/ko"
|
||||||
|
rm -rf "$PublishDir/pl"
|
||||||
|
rm -rf "$PublishDir/pt-BR"
|
||||||
|
rm -rf "$PublishDir/tr"
|
||||||
|
rm -rf "$PublishDir/zh-Hans"
|
||||||
|
rm -rf "$PublishDir/zh-Hant"
|
||||||
|
if [ -d "$PublishDir/refs" ]; then mv "$PublishDir/refs" "$PublishDir/Lib/refs"; fi
|
@ -1,67 +0,0 @@
|
|||||||
set PublishDir=%1
|
|
||||||
set SourceDir=%2
|
|
||||||
SET COPYCMD=/Y
|
|
||||||
|
|
||||||
echo deleting extra runtime files
|
|
||||||
if exist "%PublishDir%\runtimes\linux-arm" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\linux-arm'
|
|
||||||
if exist "%PublishDir%\runtimes\linux-arm64" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\linux-arm64'
|
|
||||||
if exist "%PublishDir%\runtimes\linux-armel" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\linux-armel'
|
|
||||||
if exist "%PublishDir%\runtimes\osx" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\osx'
|
|
||||||
if exist "%PublishDir%\runtimes\osx-x64" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\osx-x64'
|
|
||||||
if exist "%PublishDir%\runtimes\win-arm" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\win-arm'
|
|
||||||
if exist "%PublishDir%\runtimes\win-arm64" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\win-arm64'
|
|
||||||
if exist "%PublishDir%\runtimes\alpine-x64" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\alpine-x64'
|
|
||||||
if exist "%PublishDir%\runtimes\linux-musl-x64" powershell Remove-Item -Force -Recurse '%PublishDir%\runtimes\linux-musl-x64'
|
|
||||||
|
|
||||||
echo deleting misc files
|
|
||||||
if exist "%PublishDir%\web.config" del "%PublishDir%\web.config"
|
|
||||||
if exist "%PublishDir%\libman.json" del "%PublishDir%\libman.json"
|
|
||||||
del "%PublishDir%\*.exe"
|
|
||||||
del "%PublishDir%\*.pdb"
|
|
||||||
|
|
||||||
echo setting up default folders
|
|
||||||
if not exist "%PublishDir%\Configuration" md "%PublishDir%\Configuration"
|
|
||||||
move "%PublishDir%\DefaultSettings.json" "%PublishDir%\Configuration\"
|
|
||||||
if not exist "%PublishDir%\Lib\" md "%PublishDir%\Lib\"
|
|
||||||
del "%PublishDir%\Microsoft.CodeAnalysis*.dll" /F /Q
|
|
||||||
move "%PublishDir%\*.dll" "%PublishDir%\Lib\"
|
|
||||||
move "%PublishDir%\*.json" "%PublishDir%\Lib\"
|
|
||||||
move "%PublishDir%\runtimes" "%PublishDir%\Lib\runtimes"
|
|
||||||
move "%PublishDir%\ru" "%PublishDir%\Lib\ru"
|
|
||||||
move "%PublishDir%\de" "%PublishDir%\Lib\de"
|
|
||||||
move "%PublishDir%\pt" "%PublishDir%\Lib\pt"
|
|
||||||
move "%PublishDir%\es" "%PublishDir%\Lib\es"
|
|
||||||
rmdir /Q /S "%PublishDir%\cs"
|
|
||||||
rmdir /Q /S "%PublishDir%\fr"
|
|
||||||
rmdir /Q /S "%PublishDir%\it"
|
|
||||||
rmdir /Q /S "%PublishDir%\ja"
|
|
||||||
rmdir /Q /S "%PublishDir%\ko"
|
|
||||||
rmdir /Q /S "%PublishDir%\pl"
|
|
||||||
rmdir /Q /S "%PublishDir%\pt-BR"
|
|
||||||
rmdir /Q /S "%PublishDir%\tr"
|
|
||||||
rmdir /Q /S "%PublishDir%\zh-Hans"
|
|
||||||
rmdir /Q /S "%PublishDir%\zh-Hant"
|
|
||||||
if exist "%PublishDir%\refs" move "%PublishDir%\refs" "%PublishDir%\Lib\refs"
|
|
||||||
|
|
||||||
echo making start scripts
|
|
||||||
@(echo @echo off && echo @title IW4MAdmin && echo set DOTNET_CLI_TELEMETRY_OPTOUT=1 && echo dotnet Lib\IW4MAdmin.dll && echo pause) > "%PublishDir%\StartIW4MAdmin.cmd"
|
|
||||||
@(echo #!/bin/bash&& echo export DOTNET_CLI_TELEMETRY_OPTOUT=1&& echo dotnet Lib/IW4MAdmin.dll) > "%PublishDir%\StartIW4MAdmin.sh"
|
|
||||||
|
|
||||||
echo copying update scripts
|
|
||||||
copy "%SourceDir%\DeploymentFiles\UpdateIW4MAdmin.ps1" "%PublishDir%\UpdateIW4MAdmin.ps1"
|
|
||||||
copy "%SourceDir%\DeploymentFiles\UpdateIW4MAdmin.sh" "%PublishDir%\UpdateIW4MAdmin.sh"
|
|
||||||
|
|
||||||
echo moving front-end library dependencies
|
|
||||||
if not exist "%PublishDir%\wwwroot\font" mkdir "%PublishDir%\wwwroot\font"
|
|
||||||
move "WebfrontCore\wwwroot\lib\open-iconic\font\fonts\*.*" "%PublishDir%\wwwroot\font\"
|
|
||||||
if exist "%PublishDir%\wwwroot\lib" rd /s /q "%PublishDir%\wwwroot\lib"
|
|
||||||
if not exist "%PublishDir%\wwwroot\css" mkdir "%PublishDir%\wwwroot\css"
|
|
||||||
move "WebfrontCore\wwwroot\css\global.min.css" "%PublishDir%\wwwroot\css\global.min.css"
|
|
||||||
if not exist "%PublishDir%\wwwroot\js" mkdir "%PublishDir%\wwwroot\js"
|
|
||||||
move "%SourceDir%\WebfrontCore\wwwroot\js\global.min.js" "%PublishDir%\wwwroot\js\global.min.js"
|
|
||||||
if not exist "%PublishDir%\wwwroot\images" mkdir "%PublishDir%\wwwroot\images"
|
|
||||||
xcopy "%SourceDir%\WebfrontCore\wwwroot\images" "%PublishDir%\wwwroot\images" /E /H /C /I
|
|
||||||
|
|
||||||
|
|
||||||
echo setting permissions...
|
|
||||||
cacls "%PublishDir%" /t /e /p Everyone:F
|
|
@ -1,6 +0,0 @@
|
|||||||
set SolutionDir=%1
|
|
||||||
set ProjectDir=%2
|
|
||||||
set TargetDir=%3
|
|
||||||
|
|
||||||
echo D | xcopy "%SolutionDir%Plugins\ScriptPlugins\*.js" "%TargetDir%Plugins" /y
|
|
||||||
powershell -File "%ProjectDir%BuildScripts\DownloadTranslations.ps1" %TargetDir%
|
|
59
Application/BuildScripts/PreBuild.ps1
Normal file
59
Application/BuildScripts/PreBuild.ps1
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
param ( [string]$SolutionDir, [string]$OutputDir )
|
||||||
|
|
||||||
|
if (-not (Test-Path "$SolutionDir/WebfrontCore/wwwroot/font")) {
|
||||||
|
Write-Output "restoring web dependencies"
|
||||||
|
dotnet tool install Microsoft.Web.LibraryManager.Cli --global
|
||||||
|
Set-Location "$SolutionDir/WebfrontCore"
|
||||||
|
libman restore
|
||||||
|
Set-Location $SolutionDir
|
||||||
|
Copy-Item -Recurse -Force -Path "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/fonts" "$SolutionDir/WebfrontCore/wwwroot/font"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Test-Path "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss")) {
|
||||||
|
Write-Output "load external resources"
|
||||||
|
New-Item -ItemType Directory -Force -Path "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css"
|
||||||
|
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss" -OutFile "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss"
|
||||||
|
(Get-Content "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss") -replace '../fonts/', '/font/' | Set-Content "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss"
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "compiling scss files"
|
||||||
|
|
||||||
|
dotnet tool install Excubo.WebCompiler --global
|
||||||
|
webcompiler -r "$SolutionDir/WebfrontCore/wwwroot/css/src" -o WebfrontCore/wwwroot/css/ -m disable -z disable
|
||||||
|
webcompiler "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss" -o "$SolutionDir/WebfrontCore/wwwroot/css/" -m disable -z disable
|
||||||
|
|
||||||
|
if (-not (Test-Path "$SolutionDir/bundle/dotnet-bundle.dll")) {
|
||||||
|
New-Item -ItemType Directory -Force -Path "$SolutionDir/bundle"
|
||||||
|
Write-Output "getting dotnet bundle"
|
||||||
|
Invoke-WebRequest -Uri "https://raidmax.org/IW4MAdmin/res/dotnet-bundle.zip" -OutFile "$SolutionDir/bundle/dotnet-bundle.zip"
|
||||||
|
Write-Output "unzipping download"
|
||||||
|
Expand-Archive -Path "$SolutionDir/bundle/dotnet-bundle.zip" -DestinationPath "$SolutionDir/bundle" -Force
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "executing dotnet-bundle"
|
||||||
|
Set-Location "$SolutionDir/bundle"
|
||||||
|
dotnet "dotnet-bundle.dll" clean "$SolutionDir/WebfrontCore/bundleconfig.json"
|
||||||
|
dotnet "dotnet-bundle.dll" "$SolutionDir/WebfrontCore/bundleconfig.json"
|
||||||
|
Set-Location $SolutionDir
|
||||||
|
|
||||||
|
New-Item -ItemType Directory -Force -Path "$SolutionDir/BUILD/Plugins"
|
||||||
|
Write-Output "building plugins"
|
||||||
|
Set-Location "$SolutionDir/Plugins"
|
||||||
|
Get-ChildItem -Recurse -Filter *.csproj | ForEach-Object { dotnet publish $_.FullName -o "$SolutionDir/BUILD/Plugins" --no-restore }
|
||||||
|
Set-Location $SolutionDir
|
||||||
|
|
||||||
|
if (-not (Test-Path "$OutputDir/Localization")) {
|
||||||
|
Write-Output "downloading translations"
|
||||||
|
New-Item -ItemType Directory -Force -Path "$OutputDir/Localization"
|
||||||
|
$localizations = @("en-US", "ru-RU", "es-EC", "pt-BR", "de-DE")
|
||||||
|
foreach ($localization in $localizations) {
|
||||||
|
$url = "https://master.iw4.zip/localization/$localization"
|
||||||
|
$filePath = "$OutputDir/Localization/IW4MAdmin.$localization.json"
|
||||||
|
Invoke-WebRequest -Uri $url -OutFile $filePath -UseBasicParsing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Output "copying plugins to build dir"
|
||||||
|
New-Item -ItemType Directory -Force -Path "$OutputDir/Plugins"
|
||||||
|
Copy-Item -Recurse -Force -Path "$SolutionDir/BUILD/Plugins/*.dll" -Destination "$OutputDir/Plugins/"
|
||||||
|
Copy-Item -Recurse -Force -Path "$SolutionDir/Plugins/ScriptPlugins/*.js" -Destination "$OutputDir/Plugins/"
|
63
Application/BuildScripts/PreBuild.sh
Normal file
63
Application/BuildScripts/PreBuild.sh
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
SolutionDir="$1"
|
||||||
|
OutputDir="$2"
|
||||||
|
export PATH="$PATH:~/.dotnet/tools"
|
||||||
|
|
||||||
|
if [ ! -d "$SolutionDir/WebfrontCore/wwwroot/lib/font" ]; then
|
||||||
|
echo restoring web dependencies
|
||||||
|
dotnet tool install Microsoft.Web.LibraryManager.Cli --global
|
||||||
|
cd "$SolutionDir/WebfrontCore" || exit
|
||||||
|
libman restore
|
||||||
|
cd "$SolutionDir" || exit
|
||||||
|
cp -r "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/fonts" "$SolutionDir/WebfrontCore/wwwroot/font"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss" ]; then
|
||||||
|
echo load external resources
|
||||||
|
mkdir -p "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css"
|
||||||
|
curl -o "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss" https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss
|
||||||
|
sed -i 's#../fonts/#/font/#g' "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo compiling scss files
|
||||||
|
|
||||||
|
dotnet tool install Excubo.WebCompiler --global
|
||||||
|
webcompiler -r "$SolutionDir/WebfrontCore/wwwroot/css/src" -o "$SolutionDir/WebfrontCore/wwwroot/css/" -m disable -z disable
|
||||||
|
webcompiler "$SolutionDir/WebfrontCore/wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss" -o "$SolutionDir/WebfrontCore/wwwroot/css/" -m disable -z disable
|
||||||
|
|
||||||
|
if [ ! -f "$SolutionDir/bundle/dotnet-bundle.dll" ]; then
|
||||||
|
mkdir -p "$SolutionDir/bundle"
|
||||||
|
echo getting dotnet bundle
|
||||||
|
curl -o "$SolutionDir/bundle/dotnet-bundle.zip" https://raidmax.org/IW4MAdmin/res/dotnet-bundle.zip
|
||||||
|
echo unzipping download
|
||||||
|
unzip "$SolutionDir/bundle/dotnet-bundle.zip" -d "$SolutionDir/bundle"
|
||||||
|
fi
|
||||||
|
echo executing dotnet-bundle
|
||||||
|
cd "$SolutionDir/bundle" || exit
|
||||||
|
dotnet dotnet-bundle.dll clean "$SolutionDir/WebfrontCore/bundleconfig.json"
|
||||||
|
dotnet dotnet-bundle.dll "$SolutionDir/WebfrontCore/bundleconfig.json"
|
||||||
|
cd "$SolutionDir" || exit
|
||||||
|
|
||||||
|
mkdir -p "$SolutionDir/BUILD/Plugins"
|
||||||
|
echo building plugins
|
||||||
|
cd "$SolutionDir/Plugins" || exit
|
||||||
|
find . -name "*.csproj" -print0 | xargs -0 -I {} dotnet publish {} -o "$SolutionDir/BUILD/Plugins" --no-restore
|
||||||
|
cd "$SolutionDir" || exit
|
||||||
|
|
||||||
|
if [ ! -d "$OutputDir/Localization" ]; then
|
||||||
|
echo downloading translations
|
||||||
|
mkdir -p "$OutputDir/Localization"
|
||||||
|
localizations=("en-US" "ru-RU" "es-EC" "pt-BR" "de-DE")
|
||||||
|
for localization in "${localizations[@]}"
|
||||||
|
do
|
||||||
|
url="https://master.iw4.zip/localization/$localization"
|
||||||
|
filePath="$OutputDir/Localization/IW4MAdmin.$localization.json"
|
||||||
|
curl -s "$url" -o "$filePath"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo copying plugins to buld dir
|
||||||
|
mkdir -p "$OutputDir/Plugins"
|
||||||
|
cp -r "$SolutionDir/BUILD/Plugins/*.dll" "$OutputDir/Plugins/"
|
||||||
|
cp -r "$SolutionDir/Plugins/ScriptPlugins/*.js" "$OutputDir/Plugins/"
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SharedLibraryCore.Events.Game;
|
using SharedLibraryCore.Events.Game;
|
||||||
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
using static System.Int32;
|
using static System.Int32;
|
||||||
using static SharedLibraryCore.Server;
|
using static SharedLibraryCore.Server;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
@ -21,16 +22,18 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ApplicationConfiguration _appConfig;
|
private readonly ApplicationConfiguration _appConfig;
|
||||||
|
private readonly IGameScriptEventFactory _gameScriptEventFactory;
|
||||||
private readonly Dictionary<ParserRegex, GameEvent.EventType> _regexMap;
|
private readonly Dictionary<ParserRegex, GameEvent.EventType> _regexMap;
|
||||||
private readonly Dictionary<string, GameEvent.EventType> _eventTypeMap;
|
private readonly Dictionary<string, GameEvent.EventType> _eventTypeMap;
|
||||||
|
|
||||||
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger,
|
public BaseEventParser(IParserRegexFactory parserRegexFactory, ILogger logger,
|
||||||
ApplicationConfiguration appConfig)
|
ApplicationConfiguration appConfig, IGameScriptEventFactory gameScriptEventFactory)
|
||||||
{
|
{
|
||||||
_customEventRegistrations =
|
_customEventRegistrations =
|
||||||
new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
|
new Dictionary<string, (string, Func<string, IEventParserConfiguration, GameEvent, GameEvent>)>();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_appConfig = appConfig;
|
_appConfig = appConfig;
|
||||||
|
_gameScriptEventFactory = gameScriptEventFactory;
|
||||||
|
|
||||||
Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
|
Configuration = new DynamicEventParserConfiguration(parserRegexFactory)
|
||||||
{
|
{
|
||||||
@ -183,14 +186,28 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
|
|
||||||
if (logLine.StartsWith("GSE;"))
|
if (logLine.StartsWith("GSE;"))
|
||||||
{
|
{
|
||||||
return new GameScriptEvent
|
var gscEvent = new GameScriptEvent
|
||||||
{
|
{
|
||||||
ScriptData = logLine,
|
ScriptData = logLine,
|
||||||
GameTime = gameTime,
|
GameTime = gameTime,
|
||||||
Source = GameEvent.EventSource.Log
|
Source = GameEvent.EventSource.Log
|
||||||
};
|
};
|
||||||
|
return gscEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var split = logLine.Split(";", StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if (split.Length > 1)
|
||||||
|
{
|
||||||
|
var createdEvent = _gameScriptEventFactory.Create(split[0], logLine.Replace(split[0], ""));
|
||||||
|
if (createdEvent is not null)
|
||||||
|
{
|
||||||
|
createdEvent.ParseArguments();
|
||||||
|
return createdEvent as GameEventV2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (eventKey is null || !_customEventRegistrations.ContainsKey(eventKey))
|
if (eventKey is null || !_customEventRegistrations.ContainsKey(eventKey))
|
||||||
{
|
{
|
||||||
return GenerateDefaultEvent(logLine, gameTime);
|
return GenerateDefaultEvent(logLine, gameTime);
|
||||||
@ -218,12 +235,13 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
return GenerateDefaultEvent(logLine, gameTime);
|
return GenerateDefaultEvent(logLine, gameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GameEvent GenerateDefaultEvent(string logLine, long gameTime)
|
private static GameLogEvent GenerateDefaultEvent(string logLine, long gameTime)
|
||||||
{
|
{
|
||||||
return new GameEvent
|
return new GameLogEvent
|
||||||
{
|
{
|
||||||
Type = GameEvent.EventType.Unknown,
|
Type = GameEvent.EventType.Unknown,
|
||||||
Data = logLine,
|
Data = logLine,
|
||||||
|
LogLine = logLine,
|
||||||
Origin = Utilities.IW4MAdminClient(),
|
Origin = Utilities.IW4MAdminClient(),
|
||||||
Target = Utilities.IW4MAdminClient(),
|
Target = Utilities.IW4MAdminClient(),
|
||||||
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
RequiredEntity = GameEvent.EventRequiredEntity.None,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.EventParsers
|
namespace IW4MAdmin.Application.EventParsers
|
||||||
@ -10,7 +11,9 @@ namespace IW4MAdmin.Application.EventParsers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
sealed internal class DynamicEventParser : BaseEventParser
|
sealed internal class DynamicEventParser : BaseEventParser
|
||||||
{
|
{
|
||||||
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger, ApplicationConfiguration appConfig) : base(parserRegexFactory, logger, appConfig)
|
public DynamicEventParser(IParserRegexFactory parserRegexFactory, ILogger logger,
|
||||||
|
ApplicationConfiguration appConfig, IGameScriptEventFactory gameScriptEventFactory) : base(
|
||||||
|
parserRegexFactory, logger, appConfig, gameScriptEventFactory)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Data.Models.Client;
|
||||||
using Data.Models.Client.Stats;
|
using Data.Models.Client.Stats;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Extensions;
|
namespace IW4MAdmin.Application.Extensions;
|
||||||
|
|
||||||
@ -25,4 +27,16 @@ public static class ScriptPluginExtensions
|
|||||||
{
|
{
|
||||||
return set.Where(stat => clientIds.Contains(stat.ClientId) && stat.ServerId == (long)serverId).ToList();
|
return set.Where(stat => clientIds.Contains(stat.ClientId) && stat.ServerId == (long)serverId).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static EFClient GetClientByNumber(this IGameServer server, int clientNumber) =>
|
||||||
|
server.ConnectedClients.FirstOrDefault(client => client.ClientNumber == clientNumber);
|
||||||
|
|
||||||
|
public static EFClient GetClientByGuid(this IGameServer server, string clientGuid) =>
|
||||||
|
server.ConnectedClients.FirstOrDefault(client => client?.GuidString == clientGuid?.Trim().ToLower());
|
||||||
|
|
||||||
|
public static EFClient GetClientByXuid(this IGameServer server, string clientGuid) =>
|
||||||
|
server.ConnectedClients.FirstOrDefault(client => client?.XuidString == clientGuid?.Trim().ToLower());
|
||||||
|
|
||||||
|
public static EFClient GetClientByDecimalGuid(this IGameServer server, string clientGuid) =>
|
||||||
|
server.ConnectedClients.FirstOrDefault(client => client.NetworkId.ToString() == clientGuid?.Trim().ToLower());
|
||||||
}
|
}
|
||||||
|
39
Application/Factories/GameScriptEventFactory.cs
Normal file
39
Application/Factories/GameScriptEventFactory.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Application.Factories;
|
||||||
|
|
||||||
|
public class GameScriptEventFactory : IGameScriptEventFactory
|
||||||
|
{
|
||||||
|
private readonly IServiceProvider _serviceProvider;
|
||||||
|
private readonly Dictionary<string, Type> _gameScriptEventMap;
|
||||||
|
|
||||||
|
public GameScriptEventFactory(IServiceProvider serviceProvider, IEnumerable<IGameScriptEvent> gameScriptEventTypes)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
|
||||||
|
_gameScriptEventMap = gameScriptEventTypes
|
||||||
|
.ToLookup(kvp => kvp.EventName ?? kvp.GetType().Name.Replace("ScriptEvent", ""))
|
||||||
|
.ToDictionary(kvp => kvp.Key, kvp => kvp.First().GetType());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IGameScriptEvent Create(string eventType, string logData)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(eventType) || !_gameScriptEventMap.TryGetValue(eventType, out var matchedType))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newEvent = _serviceProvider.GetRequiredService(matchedType) as IGameScriptEvent;
|
||||||
|
|
||||||
|
if (newEvent is not null)
|
||||||
|
{
|
||||||
|
newEvent.ScriptData = logData;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newEvent;
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,6 @@ public class BaseConfigurationHandlerV2<TConfigurationType> : IConfigurationHand
|
|||||||
await using var fileStream = File.OpenRead(_path);
|
await using var fileStream = File.OpenRead(_path);
|
||||||
readConfiguration =
|
readConfiguration =
|
||||||
await JsonSerializer.DeserializeAsync<TConfigurationType>(fileStream, _serializerOptions);
|
await JsonSerializer.DeserializeAsync<TConfigurationType>(fileStream, _serializerOptions);
|
||||||
await fileStream.DisposeAsync();
|
|
||||||
_watcher.Register(_path, FileUpdated);
|
_watcher.Register(_path, FileUpdated);
|
||||||
|
|
||||||
if (readConfiguration is null)
|
if (readConfiguration is null)
|
||||||
@ -131,7 +130,6 @@ public class BaseConfigurationHandlerV2<TConfigurationType> : IConfigurationHand
|
|||||||
|
|
||||||
await using var fileStream = File.Create(_path);
|
await using var fileStream = File.Create(_path);
|
||||||
await JsonSerializer.SerializeAsync(fileStream, configuration, _serializerOptions);
|
await JsonSerializer.SerializeAsync(fileStream, configuration, _serializerOptions);
|
||||||
await fileStream.DisposeAsync();
|
|
||||||
_configurationInstance = configuration;
|
_configurationInstance = configuration;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -155,7 +153,6 @@ public class BaseConfigurationHandlerV2<TConfigurationType> : IConfigurationHand
|
|||||||
await using var fileStream = File.OpenRead(_path);
|
await using var fileStream = File.OpenRead(_path);
|
||||||
var readConfiguration =
|
var readConfiguration =
|
||||||
await JsonSerializer.DeserializeAsync<TConfigurationType>(fileStream, _serializerOptions);
|
await JsonSerializer.DeserializeAsync<TConfigurationType>(fileStream, _serializerOptions);
|
||||||
await fileStream.DisposeAsync();
|
|
||||||
|
|
||||||
if (readConfiguration is null)
|
if (readConfiguration is null)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,6 @@ public sealed class ConfigurationWatcher : IDisposable
|
|||||||
};
|
};
|
||||||
|
|
||||||
_watcher.Changed += WatcherOnChanged;
|
_watcher.Changed += WatcherOnChanged;
|
||||||
_watcher.EnableRaisingEvents = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
@ -31,30 +30,28 @@ public sealed class ConfigurationWatcher : IDisposable
|
|||||||
|
|
||||||
public void Register(string fileName, Action<string> fileUpdated)
|
public void Register(string fileName, Action<string> fileUpdated)
|
||||||
{
|
{
|
||||||
if (_registeredActions.ContainsKey(fileName))
|
_registeredActions.TryAdd(fileName, fileUpdated);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_registeredActions.Add(fileName, fileUpdated);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unregister(string fileName)
|
public void Unregister(string fileName)
|
||||||
{
|
|
||||||
if (_registeredActions.ContainsKey(fileName))
|
|
||||||
{
|
{
|
||||||
_registeredActions.Remove(fileName);
|
_registeredActions.Remove(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
_watcher.EnableRaisingEvents = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void WatcherOnChanged(object sender, FileSystemEventArgs eventArgs)
|
private void WatcherOnChanged(object sender, FileSystemEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!_registeredActions.ContainsKey(eventArgs.FullPath) || eventArgs.ChangeType != WatcherChangeTypes.Changed ||
|
if (!_registeredActions.TryGetValue(eventArgs.FullPath, out var value) ||
|
||||||
|
eventArgs.ChangeType != WatcherChangeTypes.Changed ||
|
||||||
new FileInfo(eventArgs.FullPath).Length == 0)
|
new FileInfo(eventArgs.FullPath).Length == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_registeredActions[eventArgs.FullPath].Invoke(eventArgs.FullPath);
|
value.Invoke(eventArgs.FullPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,7 +82,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
{
|
{
|
||||||
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != Utilities.WORLD_ID)
|
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Origin) == GameEvent.EventRequiredEntity.Origin && gameEvent.Origin.NetworkId != Utilities.WORLD_ID)
|
||||||
{
|
{
|
||||||
gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId);;
|
gameEvent.Origin = _server.GetClientsAsList().First(_client => _client.NetworkId == gameEvent.Origin?.NetworkId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target)
|
if ((gameEvent.RequiredEntity & GameEvent.EventRequiredEntity.Target) == GameEvent.EventRequiredEntity.Target)
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using IW4MAdmin.Application.API.GameLogServer;
|
using IW4MAdmin.Application.API.GameLogServer;
|
||||||
using RestEase;
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System;
|
using System;
|
||||||
@ -7,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Refit;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.IO
|
namespace IW4MAdmin.Application.IO
|
||||||
@ -25,7 +25,7 @@ namespace IW4MAdmin.Application.IO
|
|||||||
public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger<GameLogReaderHttp> logger)
|
public GameLogReaderHttp(Uri[] gameLogServerUris, IEventParser parser, ILogger<GameLogReaderHttp> logger)
|
||||||
{
|
{
|
||||||
_eventParser = parser;
|
_eventParser = parser;
|
||||||
_logServerApi = RestClient.For<IGameLogServer>(gameLogServerUris[0].ToString());
|
_logServerApi = RestService.For<IGameLogServer>(gameLogServerUris[0].ToString());
|
||||||
_safeLogPath = gameLogServerUris[1].LocalPath.ToBase64UrlSafeString();
|
_safeLogPath = gameLogServerUris[1].LocalPath.ToBase64UrlSafeString();
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ using Serilog.Context;
|
|||||||
using static SharedLibraryCore.Database.Models.EFClient;
|
using static SharedLibraryCore.Database.Models.EFClient;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Data.Models.Server;
|
using Data.Models.Server;
|
||||||
|
using Humanizer;
|
||||||
using IW4MAdmin.Application.Alerts;
|
using IW4MAdmin.Application.Alerts;
|
||||||
using IW4MAdmin.Application.Commands;
|
using IW4MAdmin.Application.Commands;
|
||||||
using IW4MAdmin.Application.Plugin.Script;
|
using IW4MAdmin.Application.Plugin.Script;
|
||||||
@ -193,11 +194,47 @@ namespace IW4MAdmin
|
|||||||
Command command = null;
|
Command command = null;
|
||||||
if (E.Type == GameEvent.EventType.Command)
|
if (E.Type == GameEvent.EventType.Command)
|
||||||
{
|
{
|
||||||
|
if (E.Origin is not null)
|
||||||
|
{
|
||||||
|
var canExecute = true;
|
||||||
|
|
||||||
|
if (E.Origin.CommandExecutionAttempts > 0 && E.Origin.Level < Permission.Trusted)
|
||||||
|
{
|
||||||
|
var remainingTimeout =
|
||||||
|
E.Origin.LastCommandExecutionAttempt +
|
||||||
|
Utilities.GetExponentialBackoffDelay(E.Origin.CommandExecutionAttempts) -
|
||||||
|
DateTimeOffset.UtcNow;
|
||||||
|
|
||||||
|
if (remainingTimeout.TotalSeconds > 0)
|
||||||
|
{
|
||||||
|
if (E.Origin.CommandExecutionAttempts < 2 ||
|
||||||
|
E.Origin.CommandExecutionAttempts % 5 == 0)
|
||||||
|
{
|
||||||
|
E.Origin.Tell(_translationLookup["COMMANDS_BACKOFF_MESSAGE"]
|
||||||
|
.FormatExt(remainingTimeout.Humanize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
canExecute = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
E.Origin.CommandExecutionAttempts = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
E.Origin.LastCommandExecutionAttempt = DateTimeOffset.UtcNow;
|
||||||
|
E.Origin.CommandExecutionAttempts++;
|
||||||
|
|
||||||
|
if (!canExecute)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
command = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration(), _commandConfiguration);
|
command = await SharedLibraryCore.Commands.CommandProcessing.ValidateCommand(E, Manager.GetApplicationSettings().Configuration(), _commandConfiguration);
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (CommandException e)
|
catch (CommandException e)
|
||||||
{
|
{
|
||||||
ServerLogger.LogWarning(e, "Error validating command from event {@Event}",
|
ServerLogger.LogWarning(e, "Error validating command from event {@Event}",
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
@ -59,11 +60,11 @@ namespace IW4MAdmin.Application.Localization
|
|||||||
|
|
||||||
var localizationDict = new Dictionary<string, string>();
|
var localizationDict = new Dictionary<string, string>();
|
||||||
|
|
||||||
foreach (string filePath in localizationFiles)
|
foreach (var filePath in localizationFiles)
|
||||||
{
|
{
|
||||||
var localizationContents = File.ReadAllText(filePath, Encoding.UTF8);
|
var localizationContents = File.ReadAllText(filePath, Encoding.UTF8);
|
||||||
var eachLocalizationFile = Newtonsoft.Json.JsonConvert.DeserializeObject<SharedLibraryCore.Localization.Layout>(localizationContents);
|
var eachLocalizationFile = JsonSerializer.Deserialize<SharedLibraryCore.Localization.Layout>(localizationContents);
|
||||||
if (eachLocalizationFile == null)
|
if (eachLocalizationFile is null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -72,7 +73,7 @@ namespace IW4MAdmin.Application.Localization
|
|||||||
{
|
{
|
||||||
if (!localizationDict.TryAdd(item.Key, item.Value))
|
if (!localizationDict.TryAdd(item.Key, item.Value))
|
||||||
{
|
{
|
||||||
logger.LogError("Could not add locale string {key} to localization", item.Key);
|
logger.LogError("Could not add locale string {Key} to localization", item.Key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ using IW4MAdmin.Application.Meta;
|
|||||||
using IW4MAdmin.Application.Migration;
|
using IW4MAdmin.Application.Migration;
|
||||||
using IW4MAdmin.Application.Misc;
|
using IW4MAdmin.Application.Misc;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using RestEase;
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
@ -39,6 +38,8 @@ using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|||||||
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
using IW4MAdmin.Plugins.Stats.Client.Abstractions;
|
||||||
using IW4MAdmin.Plugins.Stats.Client;
|
using IW4MAdmin.Plugins.Stats.Client;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Refit;
|
||||||
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
using Stats.Client.Abstractions;
|
using Stats.Client.Abstractions;
|
||||||
using Stats.Client;
|
using Stats.Client;
|
||||||
using Stats.Config;
|
using Stats.Config;
|
||||||
@ -94,15 +95,6 @@ namespace IW4MAdmin.Application
|
|||||||
Console.WriteLine($" Version {Utilities.GetVersionAsString()}");
|
Console.WriteLine($" Version {Utilities.GetVersionAsString()}");
|
||||||
Console.WriteLine("=====================================================");
|
Console.WriteLine("=====================================================");
|
||||||
|
|
||||||
Console.ForegroundColor = ConsoleColor.Red;
|
|
||||||
Console.WriteLine("!!!! IMPORTANT !!!!");
|
|
||||||
Console.WriteLine("The next update of IW4MAdmin will require .NET 8.");
|
|
||||||
Console.WriteLine("This is a breaking change!");
|
|
||||||
Console.WriteLine(
|
|
||||||
"Please update the ASP.NET Core Runtime: https://dotnet.microsoft.com/en-us/download/dotnet/8.0");
|
|
||||||
Console.WriteLine("!!!!!!!!!!!!!!!!!!!");
|
|
||||||
Console.ForegroundColor = ConsoleColor.Gray;
|
|
||||||
|
|
||||||
await LaunchAsync();
|
await LaunchAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -451,12 +443,12 @@ namespace IW4MAdmin.Application
|
|||||||
var masterUri = Utilities.IsDevelopment
|
var masterUri = Utilities.IsDevelopment
|
||||||
? new Uri("http://127.0.0.1:8080")
|
? new Uri("http://127.0.0.1:8080")
|
||||||
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
|
: appConfig?.MasterUrl ?? new ApplicationConfiguration().MasterUrl;
|
||||||
var httpClient = new HttpClient
|
var httpClient = new HttpClient(new HttpClientHandler {AllowAutoRedirect = true})
|
||||||
{
|
{
|
||||||
BaseAddress = masterUri,
|
BaseAddress = masterUri,
|
||||||
Timeout = TimeSpan.FromSeconds(15)
|
Timeout = TimeSpan.FromSeconds(15)
|
||||||
};
|
};
|
||||||
var masterRestClient = RestClient.For<IMasterApi>(httpClient);
|
var masterRestClient = RestService.For<IMasterApi>(httpClient);
|
||||||
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
|
var translationLookup = Configure.Initialize(Utilities.DefaultLogger, masterRestClient, appConfig);
|
||||||
|
|
||||||
if (appConfig == null)
|
if (appConfig == null)
|
||||||
@ -469,10 +461,7 @@ namespace IW4MAdmin.Application
|
|||||||
// register override level names
|
// register override level names
|
||||||
foreach (var (key, value) in appConfig.OverridePermissionLevelNames)
|
foreach (var (key, value) in appConfig.OverridePermissionLevelNames)
|
||||||
{
|
{
|
||||||
if (!Utilities.PermissionLevelOverrides.ContainsKey(key))
|
Utilities.PermissionLevelOverrides.TryAdd(key, value);
|
||||||
{
|
|
||||||
Utilities.PermissionLevelOverrides.Add(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// build the dependency list
|
// build the dependency list
|
||||||
@ -539,6 +528,7 @@ namespace IW4MAdmin.Application
|
|||||||
.AddSingleton(new ConfigurationWatcher())
|
.AddSingleton(new ConfigurationWatcher())
|
||||||
.AddSingleton(typeof(IConfigurationHandlerV2<>), typeof(BaseConfigurationHandlerV2<>))
|
.AddSingleton(typeof(IConfigurationHandlerV2<>), typeof(BaseConfigurationHandlerV2<>))
|
||||||
.AddSingleton<IScriptPluginFactory, ScriptPluginFactory>()
|
.AddSingleton<IScriptPluginFactory, ScriptPluginFactory>()
|
||||||
|
.AddSingleton<IGameScriptEventFactory, GameScriptEventFactory>()
|
||||||
.AddSingleton(translationLookup)
|
.AddSingleton(translationLookup)
|
||||||
.AddDatabaseContextOptions(appConfig);
|
.AddDatabaseContextOptions(appConfig);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using IW4MAdmin.Application.API.Master;
|
using IW4MAdmin.Application.API.Master;
|
||||||
using RestEase;
|
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using SharedLibraryCore.Configuration;
|
using SharedLibraryCore.Configuration;
|
||||||
using SharedLibraryCore.Helpers;
|
using SharedLibraryCore.Helpers;
|
||||||
@ -9,6 +8,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Refit;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Misc
|
namespace IW4MAdmin.Application.Misc
|
||||||
@ -28,6 +28,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
private readonly int _apiVersion = 1;
|
private readonly int _apiVersion = 1;
|
||||||
private bool _firstHeartBeat = true;
|
private bool _firstHeartBeat = true;
|
||||||
private static readonly TimeSpan Interval = TimeSpan.FromSeconds(30);
|
private static readonly TimeSpan Interval = TimeSpan.FromSeconds(30);
|
||||||
|
private string _authorizationToken;
|
||||||
|
|
||||||
public MasterCommunication(ILogger<MasterCommunication> logger, ApplicationConfiguration appConfig, ITranslationLookup translationLookup, IMasterApi apiInstance, IManager manager)
|
public MasterCommunication(ILogger<MasterCommunication> logger, ApplicationConfiguration appConfig, ITranslationLookup translationLookup, IMasterApi apiInstance, IManager manager)
|
||||||
{
|
{
|
||||||
@ -128,7 +129,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
Id = _appConfig.Id
|
Id = _appConfig.Id
|
||||||
});
|
});
|
||||||
|
|
||||||
_apiInstance.AuthorizationToken = $"Bearer {token.AccessToken}";
|
_authorizationToken = $"Bearer {token.AccessToken}";
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = new ApiInstance
|
var instance = new ApiInstance
|
||||||
@ -153,22 +154,22 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
WebfrontUrl = _appConfig.WebfrontUrl
|
WebfrontUrl = _appConfig.WebfrontUrl
|
||||||
};
|
};
|
||||||
|
|
||||||
Response<ResultMessage> response;
|
IApiResponse<ResultMessage> response;
|
||||||
|
|
||||||
if (_firstHeartBeat)
|
if (_firstHeartBeat)
|
||||||
{
|
{
|
||||||
response = await _apiInstance.AddInstance(instance);
|
response = await _apiInstance.AddInstance(instance, _authorizationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
response = await _apiInstance.UpdateInstance(instance.Id, instance);
|
response = await _apiInstance.UpdateInstance(instance.Id, instance, _authorizationToken);
|
||||||
_firstHeartBeat = false;
|
_firstHeartBeat = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.ResponseMessage.StatusCode != System.Net.HttpStatusCode.OK)
|
if (response.StatusCode != System.Net.HttpStatusCode.OK)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Non success response code from master is {StatusCode}, message is {Message}", response.ResponseMessage.StatusCode, response.StringContent);
|
_logger.LogWarning("Non success response code from master is {StatusCode}, message is {Message}", response.StatusCode, response.Error?.Content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ namespace IW4MAdmin.Application.Misc
|
|||||||
var decryptedContent = new byte[encryptedContent.Length];
|
var decryptedContent = new byte[encryptedContent.Length];
|
||||||
|
|
||||||
var keyGen = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(_appconfig.SubscriptionId), Encoding.UTF8.GetBytes(_appconfig.Id), IterationCount, HashAlgorithmName.SHA512);
|
var keyGen = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(_appconfig.SubscriptionId), Encoding.UTF8.GetBytes(_appconfig.Id), IterationCount, HashAlgorithmName.SHA512);
|
||||||
var encryption = new AesGcm(keyGen.GetBytes(KeyLength));
|
var encryption = new AesGcm(keyGen.GetBytes(KeyLength),TagLength);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,150 +1,255 @@
|
|||||||
using Newtonsoft.Json;
|
using SharedLibraryCore;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Database.Models;
|
using SharedLibraryCore.Database.Models;
|
||||||
using System;
|
using System;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
using static SharedLibraryCore.Database.Models.EFClient;
|
using static SharedLibraryCore.Database.Models.EFClient;
|
||||||
using static SharedLibraryCore.GameEvent;
|
using static SharedLibraryCore.GameEvent;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Misc
|
namespace IW4MAdmin.Application.Misc;
|
||||||
|
|
||||||
|
public class IPAddressConverter : JsonConverter<IPAddress>
|
||||||
{
|
{
|
||||||
class IPAddressConverter : JsonConverter
|
public override IPAddress Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType)
|
var ipAddressString = reader.GetString();
|
||||||
{
|
return IPAddress.Parse(ipAddressString);
|
||||||
return (objectType == typeof(IPAddress));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void Write(Utf8JsonWriter writer, IPAddress value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteValue(value.ToString());
|
writer.WriteStringValue(value.ToString());
|
||||||
}
|
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
return IPAddress.Parse((string)reader.Value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IPEndPointConverter : JsonConverter
|
public class IPEndPointConverter : JsonConverter<IPEndPoint>
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType)
|
public override IPEndPoint Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
return (objectType == typeof(IPEndPoint));
|
IPAddress address = null;
|
||||||
|
var port = 0;
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||||
|
{
|
||||||
|
var propertyName = reader.GetString();
|
||||||
|
reader.Read();
|
||||||
|
switch (propertyName)
|
||||||
|
{
|
||||||
|
case "Address":
|
||||||
|
var addressString = reader.GetString();
|
||||||
|
address = IPAddress.Parse(addressString);
|
||||||
|
break;
|
||||||
|
case "Port":
|
||||||
|
port = reader.GetInt32();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
if (reader.TokenType == JsonTokenType.EndObject)
|
||||||
{
|
{
|
||||||
IPEndPoint ep = (IPEndPoint)value;
|
break;
|
||||||
JObject jo = new JObject();
|
}
|
||||||
jo.Add("Address", JToken.FromObject(ep.Address, serializer));
|
|
||||||
jo.Add("Port", ep.Port);
|
|
||||||
jo.WriteTo(writer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
|
||||||
JObject jo = JObject.Load(reader);
|
|
||||||
IPAddress address = jo["Address"].ToObject<IPAddress>(serializer);
|
|
||||||
int port = (int)jo["Port"];
|
|
||||||
return new IPEndPoint(address, port);
|
return new IPEndPoint(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, IPEndPoint value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WriteString("Address", value.Address.ToString());
|
||||||
|
writer.WriteNumber("Port", value.Port);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClientEntityConverter : JsonConverter
|
public class ClientEntityConverter : JsonConverter<EFClient>
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType) => objectType == typeof(EFClient);
|
public override EFClient Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType,object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
{
|
||||||
if (reader.Value == null)
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var jsonObject = JObject.Load(reader);
|
long networkId = default;
|
||||||
|
int clientNumber = default;
|
||||||
|
ClientState state = default;
|
||||||
|
var currentAlias = new EFAlias();
|
||||||
|
int? ipAddress = null;
|
||||||
|
string name = null;
|
||||||
|
|
||||||
|
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||||
|
{
|
||||||
|
var propertyName = reader.GetString();
|
||||||
|
reader.Read(); // Advance to the value.
|
||||||
|
switch (propertyName)
|
||||||
|
{
|
||||||
|
case "NetworkId":
|
||||||
|
networkId = reader.GetInt64();
|
||||||
|
break;
|
||||||
|
case "ClientNumber":
|
||||||
|
clientNumber = reader.GetInt32();
|
||||||
|
break;
|
||||||
|
case "State":
|
||||||
|
state = (ClientState)reader.GetInt32();
|
||||||
|
break;
|
||||||
|
case "IPAddress":
|
||||||
|
ipAddress = reader.TokenType != JsonTokenType.Null ? reader.GetInt32() : null;
|
||||||
|
break;
|
||||||
|
case "Name":
|
||||||
|
name = reader.GetString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAlias.IPAddress = ipAddress;
|
||||||
|
currentAlias.Name = name;
|
||||||
|
|
||||||
return new EFClient
|
return new EFClient
|
||||||
{
|
{
|
||||||
NetworkId = (long)jsonObject["NetworkId"],
|
NetworkId = networkId,
|
||||||
ClientNumber = (int)jsonObject["ClientNumber"],
|
ClientNumber = clientNumber,
|
||||||
State = Enum.Parse<ClientState>(jsonObject["state"].ToString()),
|
State = state,
|
||||||
CurrentAlias = new EFAlias()
|
CurrentAlias = currentAlias
|
||||||
{
|
|
||||||
IPAddress = (int?)jsonObject["IPAddress"],
|
|
||||||
Name = jsonObject["Name"].ToString()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public override void Write(Utf8JsonWriter writer, EFClient value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var client = value as EFClient;
|
writer.WriteStartObject();
|
||||||
var jsonObject = new JObject
|
|
||||||
{
|
|
||||||
{ "NetworkId", client.NetworkId },
|
|
||||||
{ "ClientNumber", client.ClientNumber },
|
|
||||||
{ "IPAddress", client.CurrentAlias?.IPAddress },
|
|
||||||
{ "Name", client.CurrentAlias?.Name },
|
|
||||||
{ "State", (int)client.State }
|
|
||||||
};
|
|
||||||
|
|
||||||
jsonObject.WriteTo(writer);
|
writer.WriteNumber("NetworkId", value.NetworkId);
|
||||||
|
writer.WriteNumber("ClientNumber", value.ClientNumber);
|
||||||
|
writer.WriteString("State", value.State.ToString());
|
||||||
|
|
||||||
|
if (value.CurrentAlias != null)
|
||||||
|
{
|
||||||
|
writer.WriteNumber("IPAddress", value.CurrentAlias.IPAddress ?? 0);
|
||||||
|
writer.WriteString("Name", value.CurrentAlias.Name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNull("IPAddress");
|
||||||
|
writer.WriteNull("Name");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GameEventConverter : JsonConverter
|
public class GameEventConverter : JsonConverter<GameEvent>
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType) =>objectType == typeof(GameEvent);
|
public override GameEvent Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
||||||
{
|
{
|
||||||
var jsonObject = JObject.Load(reader);
|
if (reader.TokenType == JsonTokenType.Null)
|
||||||
|
|
||||||
return new GameEvent
|
|
||||||
{
|
{
|
||||||
Type = Enum.Parse<EventType>(jsonObject["Type"].ToString()),
|
return null;
|
||||||
Subtype = jsonObject["Subtype"]?.ToString(),
|
|
||||||
Source = Enum.Parse<EventSource>(jsonObject["Source"].ToString()),
|
|
||||||
RequiredEntity = Enum.Parse<EventRequiredEntity>(jsonObject["RequiredEntity"].ToString()),
|
|
||||||
Data = jsonObject["Data"].ToString(),
|
|
||||||
Message = jsonObject["Message"].ToString(),
|
|
||||||
GameTime = (int?)jsonObject["GameTime"],
|
|
||||||
Origin = jsonObject["Origin"]?.ToObject<EFClient>(serializer),
|
|
||||||
Target = jsonObject["Target"]?.ToObject<EFClient>(serializer),
|
|
||||||
ImpersonationOrigin = jsonObject["ImpersonationOrigin"]?.ToObject<EFClient>(serializer),
|
|
||||||
IsRemote = (bool)jsonObject["IsRemote"],
|
|
||||||
Extra = null, // fix
|
|
||||||
Time = (DateTime)jsonObject["Time"],
|
|
||||||
IsBlocking = (bool)jsonObject["IsBlocking"]
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
var gameEvent = new GameEvent();
|
||||||
|
while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
|
||||||
{
|
{
|
||||||
var gameEvent = value as GameEvent;
|
if (reader.TokenType == JsonTokenType.PropertyName)
|
||||||
|
|
||||||
var jsonObject = new JObject
|
|
||||||
{
|
{
|
||||||
{ "Type", (int)gameEvent.Type },
|
var propertyName = reader.GetString();
|
||||||
{ "Subtype", gameEvent.Subtype },
|
reader.Read();
|
||||||
{ "Source", (int)gameEvent.Source },
|
switch (propertyName)
|
||||||
{ "RequiredEntity", (int)gameEvent.RequiredEntity },
|
{
|
||||||
{ "Data", gameEvent.Data },
|
case "Type":
|
||||||
{ "Message", gameEvent.Message },
|
gameEvent.Type = (EventType)reader.GetInt32();
|
||||||
{ "GameTime", gameEvent.GameTime },
|
break;
|
||||||
{ "Origin", gameEvent.Origin != null ? JToken.FromObject(gameEvent.Origin, serializer) : null },
|
case "Subtype":
|
||||||
{ "Target", gameEvent.Target != null ? JToken.FromObject(gameEvent.Target, serializer) : null },
|
gameEvent.Subtype = reader.GetString();
|
||||||
{ "ImpersonationOrigin", gameEvent.ImpersonationOrigin != null ? JToken.FromObject(gameEvent.ImpersonationOrigin, serializer) : null},
|
break;
|
||||||
{ "IsRemote", gameEvent.IsRemote },
|
case "Source":
|
||||||
{ "Extra", gameEvent.Extra?.ToString() },
|
gameEvent.Source = (EventSource)reader.GetInt32();
|
||||||
{ "Time", gameEvent.Time },
|
break;
|
||||||
{ "IsBlocking", gameEvent.IsBlocking }
|
case "RequiredEntity":
|
||||||
};
|
gameEvent.RequiredEntity = (EventRequiredEntity)reader.GetInt32();
|
||||||
|
break;
|
||||||
jsonObject.WriteTo(writer);
|
case "Data":
|
||||||
|
gameEvent.Data = reader.GetString();
|
||||||
|
break;
|
||||||
|
case "Message":
|
||||||
|
gameEvent.Message = reader.GetString();
|
||||||
|
break;
|
||||||
|
case "GameTime":
|
||||||
|
gameEvent.GameTime = reader.TokenType != JsonTokenType.Null ? reader.GetInt32() : null;
|
||||||
|
break;
|
||||||
|
case "Origin":
|
||||||
|
gameEvent.Origin = JsonSerializer.Deserialize<EFClient>(ref reader, options);
|
||||||
|
break;
|
||||||
|
case "Target":
|
||||||
|
gameEvent.Target = JsonSerializer.Deserialize<EFClient>(ref reader, options);
|
||||||
|
break;
|
||||||
|
case "ImpersonationOrigin":
|
||||||
|
gameEvent.ImpersonationOrigin = JsonSerializer.Deserialize<EFClient>(ref reader, options);
|
||||||
|
break;
|
||||||
|
case "IsRemote":
|
||||||
|
gameEvent.IsRemote = reader.GetBoolean();
|
||||||
|
break;
|
||||||
|
case "Time":
|
||||||
|
gameEvent.Time = reader.GetDateTime();
|
||||||
|
break;
|
||||||
|
case "IsBlocking":
|
||||||
|
gameEvent.IsBlocking = reader.GetBoolean();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return gameEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, GameEvent value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
|
||||||
|
writer.WriteNumber("Type", (int)value.Type);
|
||||||
|
writer.WriteString("Subtype", value.Subtype);
|
||||||
|
writer.WriteNumber("Source", (int)value.Source);
|
||||||
|
writer.WriteNumber("RequiredEntity", (int)value.RequiredEntity);
|
||||||
|
writer.WriteString("Data", value.Data);
|
||||||
|
writer.WriteString("Message", value.Message);
|
||||||
|
if (value.GameTime.HasValue)
|
||||||
|
{
|
||||||
|
writer.WriteNumber("GameTime", value.GameTime.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writer.WriteNull("GameTime");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.Origin != null)
|
||||||
|
{
|
||||||
|
writer.WritePropertyName("Origin");
|
||||||
|
JsonSerializer.Serialize(writer, value.Origin, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.Target != null)
|
||||||
|
{
|
||||||
|
writer.WritePropertyName("Target");
|
||||||
|
JsonSerializer.Serialize(writer, value.Target, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.ImpersonationOrigin != null)
|
||||||
|
{
|
||||||
|
writer.WritePropertyName("ImpersonationOrigin");
|
||||||
|
JsonSerializer.Serialize(writer, value.ImpersonationOrigin, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteBoolean("IsRemote", value.IsRemote);
|
||||||
|
writer.WriteString("Time", value.Time.ToString("o"));
|
||||||
|
writer.WriteBoolean("IsBlocking", value.IsBlocking);
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -167,7 +167,7 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pluginSubscription ??= _masterApi
|
_pluginSubscription ??= _masterApi
|
||||||
.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
.GetPluginSubscription(_appConfig.Id, _appConfig.SubscriptionId).Result;
|
||||||
|
|
||||||
return _remoteAssemblyHandler.DecryptAssemblies(_pluginSubscription
|
return _remoteAssemblyHandler.DecryptAssemblies(_pluginSubscription
|
||||||
.Where(sub => sub.Type == PluginType.Binary).Select(sub => sub.Content).ToArray());
|
.Where(sub => sub.Type == PluginType.Binary).Select(sub => sub.Content).ToArray());
|
||||||
@ -185,7 +185,7 @@ namespace IW4MAdmin.Application.Plugin
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_pluginSubscription ??= _masterApi
|
_pluginSubscription ??= _masterApi
|
||||||
.GetPluginSubscription(Guid.Parse(_appConfig.Id), _appConfig.SubscriptionId).Result;
|
.GetPluginSubscription(_appConfig.Id, _appConfig.SubscriptionId).Result;
|
||||||
|
|
||||||
return _remoteAssemblyHandler.DecryptScripts(_pluginSubscription
|
return _remoteAssemblyHandler.DecryptScripts(_pluginSubscription
|
||||||
.Where(sub => sub.Type == PluginType.Script).Select(sub => sub.Content).ToArray());
|
.Where(sub => sub.Type == PluginType.Script).Select(sub => sub.Content).ToArray());
|
||||||
|
@ -23,6 +23,7 @@ using SharedLibraryCore.Database.Models;
|
|||||||
using SharedLibraryCore.Exceptions;
|
using SharedLibraryCore.Exceptions;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
|
using Reference = Data.Models.Reference;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Plugin.Script
|
namespace IW4MAdmin.Application.Plugin.Script
|
||||||
{
|
{
|
||||||
|
@ -26,6 +26,7 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using SharedLibraryCore.Interfaces.Events;
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
||||||
using JavascriptEngine = Jint.Engine;
|
using JavascriptEngine = Jint.Engine;
|
||||||
|
using Reference = Data.Models.Reference;
|
||||||
|
|
||||||
namespace IW4MAdmin.Application.Plugin.Script;
|
namespace IW4MAdmin.Application.Plugin.Script;
|
||||||
|
|
||||||
|
@ -91,9 +91,9 @@ public class ClientResourceQueryHelper : IResourceQueryHelper<ClientResourceRequ
|
|||||||
? iqGroupedClientAliases.OrderByDescending(clientAlias => clientAlias.Key.LastConnection)
|
? iqGroupedClientAliases.OrderByDescending(clientAlias => clientAlias.Key.LastConnection)
|
||||||
: iqGroupedClientAliases.OrderBy(clientAlias => clientAlias.Key.LastConnection);
|
: iqGroupedClientAliases.OrderBy(clientAlias => clientAlias.Key.LastConnection);
|
||||||
|
|
||||||
var clientIds = iqGroupedClientAliases.Select(g => g.Key.ClientId)
|
var clientIds = await iqGroupedClientAliases.Select(g => g.Key.ClientId)
|
||||||
.Skip(query.Offset)
|
.Skip(query.Offset)
|
||||||
.Take(query.Count);
|
.Take(query.Count).ToListAsync(); // todo: this change was for a pomelo limitation and may be addressed in future version
|
||||||
|
|
||||||
// this pulls in more records than we need, but it's more efficient than ordering grouped entities
|
// this pulls in more records than we need, but it's more efficient than ordering grouped entities
|
||||||
var clientLookups = await clientAliases
|
var clientLookups = await clientAliases
|
||||||
|
@ -175,6 +175,7 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
|
|
||||||
return new StatusResponse
|
return new StatusResponse
|
||||||
{
|
{
|
||||||
|
RawResponse = response,
|
||||||
Clients = ClientsFromStatus(response).ToArray(),
|
Clients = ClientsFromStatus(response).ToArray(),
|
||||||
Map = GetValueFromStatus<string>(response, ParserRegex.GroupType.RConStatusMap, Configuration.MapStatus),
|
Map = GetValueFromStatus<string>(response, ParserRegex.GroupType.RConStatusMap, Configuration.MapStatus),
|
||||||
GameType = GetValueFromStatus<string>(response, ParserRegex.GroupType.RConStatusGametype, Configuration.GametypeStatus),
|
GameType = GetValueFromStatus<string>(response, ParserRegex.GroupType.RConStatusGametype, Configuration.GametypeStatus),
|
||||||
|
@ -6,10 +6,11 @@ namespace IW4MAdmin.Application.RConParsers
|
|||||||
/// <inheritdoc cref="IStatusResponse"/>
|
/// <inheritdoc cref="IStatusResponse"/>
|
||||||
public class StatusResponse : IStatusResponse
|
public class StatusResponse : IStatusResponse
|
||||||
{
|
{
|
||||||
public string Map { get; set; }
|
public string Map { get; init; }
|
||||||
public string GameType { get; set; }
|
public string GameType { get; init; }
|
||||||
public string Hostname { get; set; }
|
public string Hostname { get; init; }
|
||||||
public int? MaxClients { get; set; }
|
public int? MaxClients { get; init; }
|
||||||
public EFClient[] Clients { get; set; }
|
public EFClient[] Clients { get; init; }
|
||||||
|
public string[] RawResponse { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
<Platforms>AnyCPU</Platforms>
|
<Platforms>AnyCPU</Platforms>
|
||||||
<Title>RaidMax.IW4MAdmin.Data</Title>
|
<Title>RaidMax.IW4MAdmin.Data</Title>
|
||||||
@ -9,16 +9,20 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="6.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.1">
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.6" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.6">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Npgsql" Version="6.0.2" />
|
<PackageReference Include="Npgsql" Version="8.0.3" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.2" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
||||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||||
#pragma warning disable CS0659
|
#pragma warning disable CS0659
|
||||||
|
|
||||||
namespace Data.Models
|
namespace Data.Models
|
||||||
{
|
{
|
||||||
public class Vector3
|
public class Vector3 : IParsable<Vector3>
|
||||||
{
|
{
|
||||||
[Key] public int Vector3Id { get; set; }
|
[Key] public int Vector3Id { get; set; }
|
||||||
public float X { get; protected set; }
|
public float X { get; protected set; }
|
||||||
@ -111,5 +112,30 @@ namespace Data.Models
|
|||||||
public double Magnitude() => Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
|
public double Magnitude() => Math.Sqrt((X * X) + (Y * Y) + (Z * Z));
|
||||||
|
|
||||||
public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude()));
|
public double AngleBetween(Vector3 a) => Math.Acos(this.DotProduct(a) / (a.Magnitude() * this.Magnitude()));
|
||||||
|
|
||||||
|
public static Vector3 Parse(string s, IFormatProvider provider)
|
||||||
|
{
|
||||||
|
return Parse(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryParse(string s, IFormatProvider provider, out Vector3 result)
|
||||||
|
{
|
||||||
|
result = new Vector3();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var parsed = Parse(s);
|
||||||
|
result.X = parsed.X;
|
||||||
|
result.Y = parsed.Y;
|
||||||
|
result.Z = parsed.Z;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
param (
|
|
||||||
[string]$PublishDir = $(throw "-PublishDir is required.")
|
|
||||||
)
|
|
||||||
|
|
||||||
md -Force ("{0}\Localization" -f $PublishDir)
|
|
||||||
|
|
||||||
$localizations = @("en-US", "ru-RU", "es-EC", "pt-BR", "de-DE")
|
|
||||||
foreach($localization in $localizations)
|
|
||||||
{
|
|
||||||
$url = "http://api.raidmax.org:5000/localization/{0}" -f $localization
|
|
||||||
$filePath = "{0}\Localization\IW4MAdmin.{1}.json" -f $PublishDir, $localization
|
|
||||||
$response = Invoke-WebRequest $url -UseBasicParsing
|
|
||||||
Out-File -FilePath $filePath -InputObject $response.Content -Encoding utf8
|
|
||||||
}
|
|
||||||
|
|
||||||
$versionInfo = (Get-Command ("{0}\IW4MAdmin.exe" -f $PublishDir)).FileVersionInfo
|
|
||||||
$json = @{
|
|
||||||
Major = $versionInfo.ProductMajorPart
|
|
||||||
Minor = $versionInfo.ProductMinorPart
|
|
||||||
Build = $versionInfo.ProductBuildPart
|
|
||||||
Revision = $versionInfo.ProductPrivatePart
|
|
||||||
}
|
|
||||||
$json | ConvertTo-Json | Out-File -FilePath ("{0}\VersionInformation.json" -f $PublishDir) -Encoding ASCII
|
|
@ -1,264 +0,0 @@
|
|||||||
name: '$(Date:yyyy.M.d)$(Rev:.r)'
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
batch: true
|
|
||||||
branches:
|
|
||||||
include:
|
|
||||||
- release/pre
|
|
||||||
- master
|
|
||||||
- develop
|
|
||||||
paths:
|
|
||||||
exclude:
|
|
||||||
- '**/*.yml'
|
|
||||||
- '*.yml'
|
|
||||||
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: 'windows-2022'
|
|
||||||
|
|
||||||
variables:
|
|
||||||
solution: 'IW4MAdmin.sln'
|
|
||||||
buildPlatform: 'Any CPU'
|
|
||||||
outputFolder: '$(Build.ArtifactStagingDirectory)\Publish\$(buildConfiguration)'
|
|
||||||
releaseType: verified
|
|
||||||
buildConfiguration: Stable
|
|
||||||
isPreRelease: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: Build_Deploy
|
|
||||||
steps:
|
|
||||||
- task: UseDotNet@2
|
|
||||||
displayName: 'Install .NET Core 6 SDK'
|
|
||||||
inputs:
|
|
||||||
packageType: 'sdk'
|
|
||||||
version: '6.0.x'
|
|
||||||
includePreviewVersions: true
|
|
||||||
|
|
||||||
- task: NuGetToolInstaller@1
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Setup Pre-Release configuration'
|
|
||||||
condition: or(eq(variables['Build.SourceBranch'], 'refs/heads/release/pre'), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
echo '##vso[task.setvariable variable=releaseType]prerelease'
|
|
||||||
echo '##vso[task.setvariable variable=buildConfiguration]Prerelease'
|
|
||||||
echo '##vso[task.setvariable variable=isPreRelease]true'
|
|
||||||
failOnStderr: true
|
|
||||||
|
|
||||||
- task: NuGetCommand@2
|
|
||||||
displayName: 'Restore nuget packages'
|
|
||||||
inputs:
|
|
||||||
restoreSolution: '$(solution)'
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Preload external resources'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
Write-Host 'Build Configuration is $(buildConfiguration), Release Type is $(releaseType)'
|
|
||||||
md -Force lib\open-iconic\font\css
|
|
||||||
wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o lib\open-iconic\font\css\open-iconic-bootstrap-override.scss
|
|
||||||
cd lib\open-iconic\font\css
|
|
||||||
(Get-Content open-iconic-bootstrap-override.scss).replace('../fonts/', '/font/') | Set-Content open-iconic-bootstrap-override.scss
|
|
||||||
failOnStderr: true
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)\WebfrontCore\wwwroot'
|
|
||||||
|
|
||||||
- task: VSBuild@1
|
|
||||||
displayName: 'Build projects'
|
|
||||||
inputs:
|
|
||||||
solution: '$(solution)'
|
|
||||||
msbuildArgs: '/p:DeployOnBuild=false /p:PackageAsSingleFile=false /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)" /p:Version=$(Build.BuildNumber) /p:PackageVersion=$(Build.BuildNumber)'
|
|
||||||
platform: '$(buildPlatform)'
|
|
||||||
configuration: '$(buildConfiguration)'
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Bundle JS Files'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
Write-Host 'Getting dotnet bundle'
|
|
||||||
wget http://raidmax.org/IW4MAdmin/res/dotnet-bundle.zip -o $(Build.Repository.LocalPath)\dotnet-bundle.zip
|
|
||||||
Write-Host 'Unzipping download'
|
|
||||||
Expand-Archive -LiteralPath $(Build.Repository.LocalPath)\dotnet-bundle.zip -DestinationPath $(Build.Repository.LocalPath)
|
|
||||||
Write-Host 'Executing dotnet-bundle'
|
|
||||||
$(Build.Repository.LocalPath)\dotnet-bundle.exe clean $(Build.Repository.LocalPath)\WebfrontCore\bundleconfig.json
|
|
||||||
$(Build.Repository.LocalPath)\dotnet-bundle.exe $(Build.Repository.LocalPath)\WebfrontCore\bundleconfig.json
|
|
||||||
failOnStderr: true
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)\WebfrontCore'
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'Publish projects'
|
|
||||||
inputs:
|
|
||||||
command: 'publish'
|
|
||||||
publishWebProjects: false
|
|
||||||
projects: |
|
|
||||||
**/WebfrontCore.csproj
|
|
||||||
**/Application.csproj
|
|
||||||
arguments: '-c $(buildConfiguration) -o $(outputFolder) /p:Version=$(Build.BuildNumber)'
|
|
||||||
zipAfterPublish: false
|
|
||||||
modifyOutputPath: false
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Run publish script 1'
|
|
||||||
inputs:
|
|
||||||
filePath: 'DeploymentFiles/PostPublish.ps1'
|
|
||||||
arguments: '$(outputFolder)'
|
|
||||||
failOnStderr: true
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)'
|
|
||||||
|
|
||||||
- task: BatchScript@1
|
|
||||||
displayName: 'Run publish script 2'
|
|
||||||
inputs:
|
|
||||||
filename: 'Application\BuildScripts\PostPublish.bat'
|
|
||||||
workingFolder: '$(Build.Repository.LocalPath)'
|
|
||||||
arguments: '$(outputFolder) $(Build.Repository.LocalPath)'
|
|
||||||
failOnStandardError: true
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Download dos2unix for line endings'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: 'wget https://raidmax.org/downloads/dos2unix.exe'
|
|
||||||
failOnStderr: true
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)\Application\BuildScripts'
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: 'Convert Linux start script line endings'
|
|
||||||
inputs:
|
|
||||||
script: |
|
|
||||||
echo changing to encoding for linux start script
|
|
||||||
dos2unix $(outputFolder)\StartIW4MAdmin.sh
|
|
||||||
dos2unix $(outputFolder)\UpdateIW4MAdmin.sh
|
|
||||||
echo creating website version filename
|
|
||||||
@echo IW4MAdmin-$(Build.BuildNumber) > $(Build.ArtifactStagingDirectory)\version_$(releaseType).txt
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)\Application\BuildScripts'
|
|
||||||
|
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Move script plugins into publish directory'
|
|
||||||
inputs:
|
|
||||||
SourceFolder: '$(Build.Repository.LocalPath)\Plugins\ScriptPlugins'
|
|
||||||
Contents: '*.js'
|
|
||||||
TargetFolder: '$(outputFolder)\Plugins'
|
|
||||||
|
|
||||||
- task: CopyFiles@2
|
|
||||||
displayName: 'Move binary plugins into publish directory'
|
|
||||||
inputs:
|
|
||||||
SourceFolder: '$(Build.Repository.LocalPath)\BUILD\Plugins\'
|
|
||||||
Contents: '*.dll'
|
|
||||||
TargetFolder: '$(outputFolder)\Plugins'
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: 'Move webfront resources into publish directory'
|
|
||||||
inputs:
|
|
||||||
script: 'xcopy /s /y /f wwwroot $(outputFolder)\wwwroot'
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)\BUILD\Plugins'
|
|
||||||
failOnStderr: true
|
|
||||||
|
|
||||||
- task: CmdLine@2
|
|
||||||
displayName: 'Move gamescript files into publish directory'
|
|
||||||
inputs:
|
|
||||||
script: 'echo d | xcopy /s /y /f GameFiles $(outputFolder)\GameFiles'
|
|
||||||
workingDirectory: '$(Build.Repository.LocalPath)'
|
|
||||||
failOnStderr: true
|
|
||||||
|
|
||||||
- task: ArchiveFiles@2
|
|
||||||
displayName: 'Generate final zip file'
|
|
||||||
inputs:
|
|
||||||
rootFolderOrFile: '$(outputFolder)'
|
|
||||||
includeRootFolder: false
|
|
||||||
archiveType: 'zip'
|
|
||||||
archiveFile: '$(Build.ArtifactStagingDirectory)/IW4MAdmin-$(Build.BuildNumber).zip'
|
|
||||||
replaceExistingArchive: true
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(Build.ArtifactStagingDirectory)/IW4MAdmin-$(Build.BuildNumber).zip'
|
|
||||||
artifact: 'IW4MAdmin-$(Build.BuildNumber).zip'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
|
||||||
displayName: 'Publish artifact for analysis'
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(outputFolder)'
|
|
||||||
artifact: 'IW4MAdmin.$(buildConfiguration)'
|
|
||||||
publishLocation: 'pipeline'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
|
||||||
displayName: 'Publish nuget package artifact'
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(Build.Repository.LocalPath)/SharedLibraryCore/bin/$(buildConfiguration)/RaidMax.IW4MAdmin.SharedLibraryCore.$(Build.BuildNumber).nupkg'
|
|
||||||
artifact: 'SharedLibraryCore.$(Build.BuildNumber).nupkg'
|
|
||||||
publishLocation: 'pipeline'
|
|
||||||
|
|
||||||
- task: FtpUpload@2
|
|
||||||
condition: ne(variables['Build.SourceBranch'], 'refs/heads/develop')
|
|
||||||
displayName: 'Upload zip file to website'
|
|
||||||
inputs:
|
|
||||||
credentialsOption: 'inputs'
|
|
||||||
serverUrl: '$(FTPUrl)'
|
|
||||||
username: '$(FTPUsername)'
|
|
||||||
password: '$(FTPPassword)'
|
|
||||||
rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
|
||||||
filePatterns: '*.zip'
|
|
||||||
remoteDirectory: 'IW4MAdmin/Download'
|
|
||||||
clean: false
|
|
||||||
cleanContents: false
|
|
||||||
preservePaths: false
|
|
||||||
trustSSL: false
|
|
||||||
|
|
||||||
- task: FtpUpload@2
|
|
||||||
condition: ne(variables['Build.SourceBranch'], 'refs/heads/develop')
|
|
||||||
displayName: 'Upload version info to website'
|
|
||||||
inputs:
|
|
||||||
credentialsOption: 'inputs'
|
|
||||||
serverUrl: '$(FTPUrl)'
|
|
||||||
username: '$(FTPUsername)'
|
|
||||||
password: '$(FTPPassword)'
|
|
||||||
rootDirectory: '$(Build.ArtifactStagingDirectory)'
|
|
||||||
filePatterns: 'version_$(releaseType).txt'
|
|
||||||
remoteDirectory: 'IW4MAdmin'
|
|
||||||
clean: false
|
|
||||||
cleanContents: false
|
|
||||||
preservePaths: false
|
|
||||||
trustSSL: false
|
|
||||||
|
|
||||||
- task: GitHubRelease@1
|
|
||||||
condition: ne(variables['Build.SourceBranch'], 'refs/heads/develop')
|
|
||||||
displayName: 'Make GitHub release'
|
|
||||||
inputs:
|
|
||||||
gitHubConnection: 'github.com_RaidMax'
|
|
||||||
repositoryName: 'RaidMax/IW4M-Admin'
|
|
||||||
action: 'create'
|
|
||||||
target: '$(Build.SourceVersion)'
|
|
||||||
tagSource: 'userSpecifiedTag'
|
|
||||||
tag: '$(Build.BuildNumber)-$(releaseType)'
|
|
||||||
title: 'IW4MAdmin $(Build.BuildNumber) ($(releaseType))'
|
|
||||||
assets: '$(Build.ArtifactStagingDirectory)/*.zip'
|
|
||||||
isPreRelease: $(isPreRelease)
|
|
||||||
releaseNotesSource: 'inline'
|
|
||||||
releaseNotesInline: 'Automated rolling release - changelog below. [Updating Instructions](https://github.com/RaidMax/IW4M-Admin/wiki/Getting-Started#updating)'
|
|
||||||
changeLogCompareToRelease: 'lastNonDraftRelease'
|
|
||||||
changeLogType: 'commitBased'
|
|
||||||
|
|
||||||
- task: PowerShell@2
|
|
||||||
condition: ne(variables['Build.SourceBranch'], 'refs/heads/develop')
|
|
||||||
displayName: 'Update master version'
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
$payload = @{
|
|
||||||
'current-version-$(releaseType)' = '$(Build.BuildNumber)'
|
|
||||||
'jwt-secret' = '$(JWTSecret)'
|
|
||||||
} | ConvertTo-Json
|
|
||||||
|
|
||||||
|
|
||||||
$params = @{
|
|
||||||
Uri = 'http://api.raidmax.org:5000/version'
|
|
||||||
Method = 'POST'
|
|
||||||
Body = $payload
|
|
||||||
ContentType = 'application/json'
|
|
||||||
}
|
|
||||||
|
|
||||||
Invoke-RestMethod @params
|
|
@ -1,55 +0,0 @@
|
|||||||
name: '$(Date:yyyy.M.d)$(Rev:.r)'
|
|
||||||
|
|
||||||
pr: none
|
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: 'windows-2022'
|
|
||||||
|
|
||||||
variables:
|
|
||||||
buildPlatform: 'Any CPU'
|
|
||||||
outputFolder: '$(Build.ArtifactStagingDirectory)\Publish\$(buildConfiguration)'
|
|
||||||
releaseType: verified
|
|
||||||
buildConfiguration: Stable
|
|
||||||
isPreRelease: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
- job: Build_Pack
|
|
||||||
steps:
|
|
||||||
- task: PowerShell@2
|
|
||||||
displayName: 'Setup Build configuration'
|
|
||||||
condition: or(eq(variables['Build.SourceBranch'], 'refs/heads/release/pre'), eq(variables['Build.SourceBranch'], 'refs/heads/develop'), eq(variables['Build.SourceBranch'], 'refs/heads/chore/nuget-pipeline'))
|
|
||||||
inputs:
|
|
||||||
targetType: 'inline'
|
|
||||||
script: |
|
|
||||||
echo '##vso[task.setvariable variable=releaseType]prerelease'
|
|
||||||
echo '##vso[task.setvariable variable=buildConfiguration]Prerelease'
|
|
||||||
echo '##vso[task.setvariable variable=isPreRelease]true'
|
|
||||||
failOnStderr: true
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'Build Data'
|
|
||||||
inputs:
|
|
||||||
command: 'build'
|
|
||||||
projects: '**/Data.csproj'
|
|
||||||
arguments: '-c $(buildConfiguration)'
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'Build SLC'
|
|
||||||
inputs:
|
|
||||||
command: 'build'
|
|
||||||
projects: '**/SharedLibraryCore.csproj'
|
|
||||||
arguments: '-c $(buildConfiguration) /p:Version=$(Build.BuildNumber)'
|
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
|
||||||
displayName: 'Pack SLC'
|
|
||||||
inputs:
|
|
||||||
command: 'pack'
|
|
||||||
packagesToPack: '**/SharedLibraryCore.csproj'
|
|
||||||
versioningScheme: 'byBuildNumber'
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
|
||||||
displayName: 'Publish nuget package artifact'
|
|
||||||
inputs:
|
|
||||||
targetPath: 'D:\a\1\a\RaidMax.IW4MAdmin.SharedLibraryCore.$(Build.BuildNumber).nupkg'
|
|
||||||
artifact: 'SharedLibraryCore.$(Build.BuildNumber).nupkg'
|
|
||||||
publishLocation: 'pipeline'
|
|
58
GameFiles/GameInterface/_integration_iw6.gsc
Normal file
58
GameFiles/GameInterface/_integration_iw6.gsc
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
Init()
|
||||||
|
{
|
||||||
|
thread Setup();
|
||||||
|
}
|
||||||
|
|
||||||
|
Setup()
|
||||||
|
{
|
||||||
|
level endon( "game_ended" );
|
||||||
|
waittillframeend;
|
||||||
|
|
||||||
|
level waittill( level.notifyTypes.sharedFunctionsInitialized );
|
||||||
|
|
||||||
|
scripts\_integration_base::RegisterLogger( ::Log2Console );
|
||||||
|
|
||||||
|
level.overrideMethods[level.commonFunctions.getTotalShotsFired] = ::GetTotalShotsFired;
|
||||||
|
level.overrideMethods[level.commonFunctions.setDvar] = ::SetDvarIfUninitializedWrapper;
|
||||||
|
level.overrideMethods[level.commonFunctions.waittillNotifyOrTimeout] = ::WaitillNotifyOrTimeoutWrapper;
|
||||||
|
level.overrideMethods[level.commonFunctions.isBot] = ::IsBotWrapper;
|
||||||
|
level.overrideMethods[level.commonFunctions.getXuid] = ::GetXuidWrapper;
|
||||||
|
level.overrideMethods[level.commonFunctions.waitTillAnyTimeout] = ::WaitTillAnyTimeout;
|
||||||
|
|
||||||
|
level notify( level.notifyTypes.gameFunctionsInitialized );
|
||||||
|
}
|
||||||
|
|
||||||
|
GetTotalShotsFired()
|
||||||
|
{
|
||||||
|
return maps\mp\_utility::getPlayerStat( "mostshotsfired" );
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDvarIfUninitializedWrapper( dvar, value )
|
||||||
|
{
|
||||||
|
SetDvarIfUninitialized( dvar, value );
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitillNotifyOrTimeoutWrapper( _notify, timeout )
|
||||||
|
{
|
||||||
|
common_scripts\utility::waittill_notify_or_timeout( _notify, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
Log2Console( logLevel, message )
|
||||||
|
{
|
||||||
|
Print( "[" + logLevel + "] " + message + "\n" );
|
||||||
|
}
|
||||||
|
|
||||||
|
IsBotWrapper( client )
|
||||||
|
{
|
||||||
|
return IsBot( client );
|
||||||
|
}
|
||||||
|
|
||||||
|
GetXuidWrapper()
|
||||||
|
{
|
||||||
|
return self GetXUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
WaitTillAnyTimeout( timeOut, string1, string2, string3, string4, string5 )
|
||||||
|
{
|
||||||
|
return common_scripts\utility::waittill_any_timeout( timeOut, string1, string2, string3, string4, string5 );
|
||||||
|
}
|
@ -6,13 +6,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C8F3945-0AEF-4949-A1F7-B18E952E50BC}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8C8F3945-0AEF-4949-A1F7-B18E952E50BC}"
|
||||||
ProjectSection(SolutionItems) = preProject
|
ProjectSection(SolutionItems) = preProject
|
||||||
DeploymentFiles\deployment-pipeline.yml = DeploymentFiles\deployment-pipeline.yml
|
|
||||||
DeploymentFiles\PostPublish.ps1 = DeploymentFiles\PostPublish.ps1
|
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
version.txt = version.txt
|
version.txt = version.txt
|
||||||
DeploymentFiles\UpdateIW4MAdmin.ps1 = DeploymentFiles\UpdateIW4MAdmin.ps1
|
DeploymentFiles\UpdateIW4MAdmin.ps1 = DeploymentFiles\UpdateIW4MAdmin.ps1
|
||||||
DeploymentFiles\UpdateIW4MAdmin.sh = DeploymentFiles\UpdateIW4MAdmin.sh
|
DeploymentFiles\UpdateIW4MAdmin.sh = DeploymentFiles\UpdateIW4MAdmin.sh
|
||||||
DeploymentFiles\nuget-pipeline.yml = DeploymentFiles\nuget-pipeline.yml
|
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLibraryCore", "SharedLibraryCore\SharedLibraryCore.csproj", "{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharedLibraryCore", "SharedLibraryCore\SharedLibraryCore.csproj", "{AA0541A2-8D51-4AD9-B0AC-3D1F5B162481}"
|
||||||
@ -112,6 +109,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pluto IW5", "Pluto IW5", "{
|
|||||||
GameFiles\AntiCheat\IW5\README.MD = GameFiles\AntiCheat\IW5\README.MD
|
GameFiles\AntiCheat\IW5\README.MD = GameFiles\AntiCheat\IW5\README.MD
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActions", "{DCCEED9F-816E-4595-8B74-D76A77FBE0BE}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.github\workflows\build_application.yml = .github\workflows\build_application.yml
|
||||||
|
.github\workflows\shared_library_nuget.yml = .github\workflows\shared_library_nuget.yml
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -461,6 +464,7 @@ Global
|
|||||||
{3EA564BD-3AC6-479B-96B6-CB059DCD0C77} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
{3EA564BD-3AC6-479B-96B6-CB059DCD0C77} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
||||||
{866F453D-BC89-457F-8B55-485494759B31} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
{866F453D-BC89-457F-8B55-485494759B31} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
||||||
{603725A4-BC0B-423B-955B-762C89E1C4C2} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
{603725A4-BC0B-423B-955B-762C89E1C4C2} = {AB83BAC0-C539-424A-BF00-78487C10753C}
|
||||||
|
{DCCEED9F-816E-4595-8B74-D76A77FBE0BE} = {8C8F3945-0AEF-4949-A1F7-B18E952E50BC}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
SolutionGuid = {84F8F8E0-1F73-41E0-BD8D-BB6676E2EE87}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyName>Integrations.Cod</AssemblyName>
|
<AssemblyName>Integrations.Cod</AssemblyName>
|
||||||
<RootNamespace>Integrations.Cod</RootNamespace>
|
<RootNamespace>Integrations.Cod</RootNamespace>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
@ -17,7 +17,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="protobuf-net" Version="3.2.26" />
|
<PackageReference Include="protobuf-net" Version="3.2.30" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<AssemblyName>Integrations.Source</AssemblyName>
|
<AssemblyName>Integrations.Source</AssemblyName>
|
||||||
<RootNamespace>Integrations.Source</RootNamespace>
|
<RootNamespace>Integrations.Source</RootNamespace>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
@ -10,11 +10,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,7 +3,6 @@ using SharedLibraryCore;
|
|||||||
using SharedLibraryCore.Dtos;
|
using SharedLibraryCore.Dtos;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using IW4MAdmin.Plugins.LiveRadar.Configuration;
|
using IW4MAdmin.Plugins.LiveRadar.Configuration;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
|
||||||
@ -82,7 +81,7 @@ namespace IW4MAdmin.Plugins.LiveRadar.Web.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var radarInfo = server.GetClientsAsList()
|
var radarInfo = server.GetClientsAsList()
|
||||||
.Select(client => client.GetAdditionalProperty<RadarEvent>("LiveRadar")).ToList();
|
.Select(client => client.GetAdditionalProperty<RadarDto>("LiveRadar")).ToList();
|
||||||
|
|
||||||
return Json(radarInfo);
|
return Json(radarInfo);
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
using SharedLibraryCore;
|
|
||||||
using SharedLibraryCore.Database.Models;
|
|
||||||
using SharedLibraryCore.Interfaces;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using SharedLibraryCore.Events.Game;
|
|
||||||
using EventGeneratorCallback = System.ValueTuple<string, string,
|
|
||||||
System.Func<string, SharedLibraryCore.Interfaces.IEventParserConfiguration,
|
|
||||||
SharedLibraryCore.GameEvent,
|
|
||||||
SharedLibraryCore.GameEvent>>;
|
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.LiveRadar.Events;
|
|
||||||
|
|
||||||
public class Script : IRegisterEvent
|
|
||||||
{
|
|
||||||
private const string EventLiveRadar = "LiveRadar";
|
|
||||||
|
|
||||||
private EventGeneratorCallback LiveRadar()
|
|
||||||
{
|
|
||||||
return (EventLiveRadar, EventLiveRadar, (eventLine, _, _) =>
|
|
||||||
{
|
|
||||||
var radarEvent = new LiveRadarEvent
|
|
||||||
{
|
|
||||||
Type = GameEvent.EventType.Other,
|
|
||||||
Subtype = EventLiveRadar,
|
|
||||||
Origin = new EFClient { NetworkId = 0 },
|
|
||||||
ScriptData = eventLine
|
|
||||||
};
|
|
||||||
return radarEvent;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<EventGeneratorCallback> Events => new[] { LiveRadar() };
|
|
||||||
}
|
|
||||||
|
|
||||||
public class LiveRadarEvent : GameScriptEvent
|
|
||||||
{
|
|
||||||
}
|
|
19
Plugins/LiveRadar/Events/LiveRadarScriptEvent.cs
Normal file
19
Plugins/LiveRadar/Events/LiveRadarScriptEvent.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using Data.Models;
|
||||||
|
using SharedLibraryCore.Events.Game;
|
||||||
|
|
||||||
|
namespace IW4MAdmin.Plugins.LiveRadar.Events;
|
||||||
|
|
||||||
|
public class LiveRadarScriptEvent : GameScriptEvent
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Vector3 Location { get; set; }
|
||||||
|
public Vector3 ViewAngles { get; set; }
|
||||||
|
public string Team { get; set; }
|
||||||
|
public int Kills { get; set; }
|
||||||
|
public int Deaths { get; set; }
|
||||||
|
public int Score { get; set; }
|
||||||
|
public string Weapon { get; set; }
|
||||||
|
public int Health { get; set; }
|
||||||
|
public bool IsAlive { get; set; }
|
||||||
|
public int PlayTime { get; set; }
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RazorCompileOnBuild Condition="'$(CONFIG)'!='Debug'">true</RazorCompileOnBuild>
|
<RazorCompileOnBuild Condition="'$(CONFIG)'!='Debug'">true</RazorCompileOnBuild>
|
||||||
<RazorCompiledOnPublish Condition="'$(CONFIG)'!='Debug'">true</RazorCompiledOnPublish>
|
<RazorCompiledOnPublish Condition="'$(CONFIG)'!='Debug'">true</RazorCompiledOnPublish>
|
||||||
<PreserveCompilationContext Condition="'$(CONFIG)'!='Debug'">false</PreserveCompilationContext>
|
<PreserveCompilationContext>false</PreserveCompilationContext>
|
||||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
<Version>0.1.0.0</Version>
|
<Version>0.1.0.0</Version>
|
||||||
@ -16,11 +16,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -36,6 +36,9 @@ public class Plugin : IPluginV2
|
|||||||
public static void RegisterDependencies(IServiceCollection serviceCollection)
|
public static void RegisterDependencies(IServiceCollection serviceCollection)
|
||||||
{
|
{
|
||||||
serviceCollection.AddConfiguration<LiveRadarConfiguration>();
|
serviceCollection.AddConfiguration<LiveRadarConfiguration>();
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton<IGameScriptEvent, LiveRadarScriptEvent>(); // for identification
|
||||||
|
serviceCollection.AddTransient<LiveRadarScriptEvent>(); // for factory
|
||||||
}
|
}
|
||||||
|
|
||||||
public Plugin(ILogger<Plugin> logger, ApplicationConfiguration appConfig)
|
public Plugin(ILogger<Plugin> logger, ApplicationConfiguration appConfig)
|
||||||
@ -51,7 +54,7 @@ public class Plugin : IPluginV2
|
|||||||
|
|
||||||
private Task OnScriptEvent(GameScriptEvent scriptEvent, CancellationToken token)
|
private Task OnScriptEvent(GameScriptEvent scriptEvent, CancellationToken token)
|
||||||
{
|
{
|
||||||
if (scriptEvent is not LiveRadarEvent radarEvent)
|
if (scriptEvent is not LiveRadarScriptEvent radarEvent)
|
||||||
{
|
{
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@ -83,14 +86,15 @@ public class Plugin : IPluginV2
|
|||||||
: (originalBotGuid ?? "0").ConvertGuidToLong(NumberStyles.HexNumber);
|
: (originalBotGuid ?? "0").ConvertGuidToLong(NumberStyles.HexNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
var radarUpdate = RadarEvent.Parse(scriptEvent.ScriptData, generatedBotGuid);
|
var radarDto = RadarDto.FromScriptEvent(radarEvent, generatedBotGuid);
|
||||||
|
|
||||||
var client =
|
var client =
|
||||||
radarEvent.Owner.ConnectedClients.FirstOrDefault(client => client.NetworkId == radarUpdate.Guid);
|
radarEvent.Owner.ConnectedClients.FirstOrDefault(client => client.NetworkId == radarDto.Guid);
|
||||||
|
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
radarUpdate.Name = client.Name.StripColors();
|
radarDto.Name = client.Name.StripColors();
|
||||||
client.SetAdditionalProperty("LiveRadar", radarUpdate);
|
client.SetAdditionalProperty("LiveRadar", radarDto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
using Data.Models;
|
using Data.Models;
|
||||||
using SharedLibraryCore;
|
using SharedLibraryCore;
|
||||||
using System;
|
using IW4MAdmin.Plugins.LiveRadar.Events;
|
||||||
using System.Linq;
|
|
||||||
// ReSharper disable CompareOfFloatsByEqualityOperator
|
// ReSharper disable CompareOfFloatsByEqualityOperator
|
||||||
#pragma warning disable CS0659
|
#pragma warning disable CS0659
|
||||||
|
|
||||||
namespace IW4MAdmin.Plugins.LiveRadar;
|
namespace IW4MAdmin.Plugins.LiveRadar;
|
||||||
|
|
||||||
public class RadarEvent
|
public class RadarDto
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public long Guid { get; set; }
|
public long Guid { get; set; }
|
||||||
@ -26,7 +26,7 @@ public class RadarEvent
|
|||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
if (obj is RadarEvent re)
|
if (obj is RadarDto re)
|
||||||
{
|
{
|
||||||
return re.ViewAngles.X == ViewAngles.X &&
|
return re.ViewAngles.X == ViewAngles.X &&
|
||||||
re.ViewAngles.Y == ViewAngles.Y &&
|
re.ViewAngles.Y == ViewAngles.Y &&
|
||||||
@ -39,23 +39,21 @@ public class RadarEvent
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static RadarEvent Parse(string input, long generatedBotGuid)
|
public static RadarDto FromScriptEvent(LiveRadarScriptEvent scriptEvent, long generatedBotGuid)
|
||||||
{
|
{
|
||||||
var items = input.Split(';').Skip(1).ToList();
|
var parsedEvent = new RadarDto
|
||||||
|
|
||||||
var parsedEvent = new RadarEvent()
|
|
||||||
{
|
{
|
||||||
Guid = generatedBotGuid,
|
Guid = generatedBotGuid,
|
||||||
Location = Vector3.Parse(items[1]),
|
Location = scriptEvent.Location,
|
||||||
ViewAngles = Vector3.Parse(items[2]).FixIW4Angles(),
|
ViewAngles = scriptEvent.ViewAngles.FixIW4Angles(),
|
||||||
Team = items[3],
|
Team = scriptEvent.Team,
|
||||||
Kills = int.Parse(items[4]),
|
Kills = scriptEvent.Kills,
|
||||||
Deaths = int.Parse(items[5]),
|
Deaths = scriptEvent.Deaths,
|
||||||
Score = int.Parse(items[6]),
|
Score = scriptEvent.Score,
|
||||||
Weapon = items[7],
|
Weapon =scriptEvent.Weapon,
|
||||||
Health = int.Parse(items[8]),
|
Health = scriptEvent.Health,
|
||||||
IsAlive = items[9] == "1",
|
IsAlive = scriptEvent.IsAlive,
|
||||||
PlayTime = Convert.ToInt32(items[10])
|
PlayTime = scriptEvent.PlayTime
|
||||||
};
|
};
|
||||||
|
|
||||||
return parsedEvent;
|
return parsedEvent;
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
<CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>
|
||||||
@ -19,11 +19,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -20,8 +20,8 @@ public class MuteCommand : Command
|
|||||||
Permission = EFClient.Permission.Moderator;
|
Permission = EFClient.Permission.Moderator;
|
||||||
RequiresTarget = true;
|
RequiresTarget = true;
|
||||||
SupportedGames = Plugin.SupportedGames;
|
SupportedGames = Plugin.SupportedGames;
|
||||||
Arguments = new[]
|
Arguments =
|
||||||
{
|
[
|
||||||
new CommandArgument
|
new CommandArgument
|
||||||
{
|
{
|
||||||
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
||||||
@ -32,7 +32,7 @@ public class MuteCommand : Command
|
|||||||
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
||||||
Required = true
|
Required = true
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||||
|
@ -21,14 +21,14 @@ public class MuteInfoCommand : Command
|
|||||||
Permission = EFClient.Permission.Moderator;
|
Permission = EFClient.Permission.Moderator;
|
||||||
RequiresTarget = true;
|
RequiresTarget = true;
|
||||||
SupportedGames = Plugin.SupportedGames;
|
SupportedGames = Plugin.SupportedGames;
|
||||||
Arguments = new[]
|
Arguments =
|
||||||
{
|
[
|
||||||
new CommandArgument
|
new CommandArgument
|
||||||
{
|
{
|
||||||
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
||||||
Required = true
|
Required = true
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||||
|
@ -7,10 +7,9 @@ using SharedLibraryCore.Interfaces;
|
|||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Mute.Commands;
|
namespace IW4MAdmin.Plugins.Mute.Commands;
|
||||||
|
|
||||||
public class TempMuteCommand : Command
|
public partial class TempMuteCommand : Command
|
||||||
{
|
{
|
||||||
private readonly MuteManager _muteManager;
|
private readonly MuteManager _muteManager;
|
||||||
private const string TempBanRegex = @"([0-9]+\w+)\ (.+)";
|
|
||||||
|
|
||||||
public TempMuteCommand(CommandConfiguration config, ITranslationLookup translationLookup, MuteManager muteManager) : base(config,
|
public TempMuteCommand(CommandConfiguration config, ITranslationLookup translationLookup, MuteManager muteManager) : base(config,
|
||||||
translationLookup)
|
translationLookup)
|
||||||
@ -22,8 +21,8 @@ public class TempMuteCommand : Command
|
|||||||
Permission = EFClient.Permission.Moderator;
|
Permission = EFClient.Permission.Moderator;
|
||||||
RequiresTarget = true;
|
RequiresTarget = true;
|
||||||
SupportedGames = Plugin.SupportedGames;
|
SupportedGames = Plugin.SupportedGames;
|
||||||
Arguments = new[]
|
Arguments =
|
||||||
{
|
[
|
||||||
new CommandArgument
|
new CommandArgument
|
||||||
{
|
{
|
||||||
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
||||||
@ -39,7 +38,7 @@ public class TempMuteCommand : Command
|
|||||||
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
||||||
Required = true
|
Required = true
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||||
@ -50,7 +49,7 @@ public class TempMuteCommand : Command
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var match = Regex.Match(gameEvent.Data, TempBanRegex);
|
var match = TempBanRegex().Match(gameEvent.Data);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
var expiration = DateTime.UtcNow + match.Groups[1].ToString().ParseTimespan();
|
var expiration = DateTime.UtcNow + match.Groups[1].ToString().ParseTimespan();
|
||||||
@ -72,4 +71,7 @@ public class TempMuteCommand : Command
|
|||||||
|
|
||||||
gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_BAD_FORMAT"]);
|
gameEvent.Origin.Tell(_translationLookup["PLUGINS_MUTE_COMMANDS_TEMPMUTE_BAD_FORMAT"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[GeneratedRegex(@"([0-9]+\w+)\ (.+)")]
|
||||||
|
private static partial Regex TempBanRegex();
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,8 @@ public class UnmuteCommand : Command
|
|||||||
Permission = EFClient.Permission.Moderator;
|
Permission = EFClient.Permission.Moderator;
|
||||||
RequiresTarget = true;
|
RequiresTarget = true;
|
||||||
SupportedGames = Plugin.SupportedGames;
|
SupportedGames = Plugin.SupportedGames;
|
||||||
Arguments = new[]
|
Arguments =
|
||||||
{
|
[
|
||||||
new CommandArgument
|
new CommandArgument
|
||||||
{
|
{
|
||||||
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
Name = translationLookup["COMMANDS_ARGS_PLAYER"],
|
||||||
@ -32,7 +32,7 @@ public class UnmuteCommand : Command
|
|||||||
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
Name = translationLookup["COMMANDS_ARGS_REASON"],
|
||||||
Required = true
|
Required = true
|
||||||
}
|
}
|
||||||
};
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ExecuteAsync(GameEvent gameEvent)
|
public override async Task ExecuteAsync(GameEvent gameEvent)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<Authors>MrAmos123</Authors>
|
<Authors>MrAmos123</Authors>
|
||||||
@ -12,10 +12,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,23 +10,15 @@ using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|||||||
|
|
||||||
namespace IW4MAdmin.Plugins.Mute;
|
namespace IW4MAdmin.Plugins.Mute;
|
||||||
|
|
||||||
public class MuteManager
|
public class MuteManager(
|
||||||
|
ILogger<MuteManager> logger,
|
||||||
|
IDatabaseContextFactory databaseContextFactory,
|
||||||
|
IMetaServiceV2 metaService,
|
||||||
|
ITranslationLookup translationLookup)
|
||||||
{
|
{
|
||||||
private readonly IMetaServiceV2 _metaService;
|
private readonly ILogger _logger = logger;
|
||||||
private readonly ITranslationLookup _translationLookup;
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
private readonly IDatabaseContextFactory _databaseContextFactory;
|
|
||||||
private readonly SemaphoreSlim _onMuteAction = new(1, 1);
|
private readonly SemaphoreSlim _onMuteAction = new(1, 1);
|
||||||
|
|
||||||
public MuteManager(ILogger<MuteManager> logger, IDatabaseContextFactory databaseContextFactory,
|
|
||||||
IMetaServiceV2 metaService, ITranslationLookup translationLookup)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_databaseContextFactory = databaseContextFactory;
|
|
||||||
_metaService = metaService;
|
|
||||||
_translationLookup = translationLookup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsExpiredMute(MuteStateMeta muteStateMeta) =>
|
public static bool IsExpiredMute(MuteStateMeta muteStateMeta) =>
|
||||||
muteStateMeta.Expiration is not null && muteStateMeta.Expiration < DateTime.UtcNow;
|
muteStateMeta.Expiration is not null && muteStateMeta.Expiration < DateTime.UtcNow;
|
||||||
|
|
||||||
@ -42,7 +34,7 @@ public class MuteManager
|
|||||||
var muteState = await ReadPersistentDataV1(client);
|
var muteState = await ReadPersistentDataV1(client);
|
||||||
clientMuteMeta = new MuteStateMeta
|
clientMuteMeta = new MuteStateMeta
|
||||||
{
|
{
|
||||||
Reason = muteState is null ? string.Empty : _translationLookup["PLUGINS_MUTE_MIGRATED"],
|
Reason = muteState is null ? string.Empty : translationLookup["PLUGINS_MUTE_MIGRATED"],
|
||||||
Expiration = muteState switch
|
Expiration = muteState switch
|
||||||
{
|
{
|
||||||
null => DateTime.UtcNow,
|
null => DateTime.UtcNow,
|
||||||
@ -149,7 +141,7 @@ public class MuteManager
|
|||||||
|
|
||||||
private async Task ExpireMutePenalties(EFClient client)
|
private async Task ExpireMutePenalties(EFClient client)
|
||||||
{
|
{
|
||||||
await using var context = _databaseContextFactory.CreateContext();
|
await using var context = databaseContextFactory.CreateContext();
|
||||||
var mutePenalties = await context.Penalties
|
var mutePenalties = await context.Penalties
|
||||||
.Where(penalty => penalty.OffenderId == client.ClientId)
|
.Where(penalty => penalty.OffenderId == client.ClientId)
|
||||||
.Where(penalty => penalty.Type == EFPenalty.PenaltyType.Mute || penalty.Type == EFPenalty.PenaltyType.TempMute)
|
.Where(penalty => penalty.Type == EFPenalty.PenaltyType.Mute || penalty.Type == EFPenalty.PenaltyType.TempMute)
|
||||||
@ -184,7 +176,7 @@ public class MuteManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async Task<MuteState?> ReadPersistentDataV1(EFClient client) => TryParse<MuteState>(
|
private async Task<MuteState?> ReadPersistentDataV1(EFClient client) => TryParse<MuteState>(
|
||||||
(await _metaService.GetPersistentMeta(Plugin.MuteKey, client.ClientId))?.Value, out var muteState)
|
(await metaService.GetPersistentMeta(Plugin.MuteKey, client.ClientId))?.Value, out var muteState)
|
||||||
? muteState
|
? muteState
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@ -195,7 +187,7 @@ public class MuteManager
|
|||||||
if (clientMuteMeta is not null) return clientMuteMeta;
|
if (clientMuteMeta is not null) return clientMuteMeta;
|
||||||
|
|
||||||
// Get meta from database and store in client if exists
|
// Get meta from database and store in client if exists
|
||||||
clientMuteMeta = await _metaService.GetPersistentMetaValue<MuteStateMeta>(Plugin.MuteKey, client.ClientId);
|
clientMuteMeta = await metaService.GetPersistentMetaValue<MuteStateMeta>(Plugin.MuteKey, client.ClientId);
|
||||||
if (clientMuteMeta is not null) client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta);
|
if (clientMuteMeta is not null) client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta);
|
||||||
|
|
||||||
return clientMuteMeta;
|
return clientMuteMeta;
|
||||||
@ -204,6 +196,6 @@ public class MuteManager
|
|||||||
private async Task WritePersistentData(EFClient client, MuteStateMeta clientMuteMeta)
|
private async Task WritePersistentData(EFClient client, MuteStateMeta clientMuteMeta)
|
||||||
{
|
{
|
||||||
client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta);
|
client.SetAdditionalProperty(Plugin.MuteKey, clientMuteMeta);
|
||||||
await _metaService.SetPersistentMetaValue(Plugin.MuteKey, clientMuteMeta, client.ClientId);
|
await metaService.SetPersistentMetaValue(Plugin.MuteKey, clientMuteMeta, client.ClientId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,15 +21,14 @@ public class Plugin : IPluginV2
|
|||||||
|
|
||||||
public const string MuteKey = "IW4MMute";
|
public const string MuteKey = "IW4MMute";
|
||||||
public static IManager Manager { get; private set; } = null!;
|
public static IManager Manager { get; private set; } = null!;
|
||||||
public static Server.Game[] SupportedGames { get; private set; } = Array.Empty<Server.Game>();
|
public static Server.Game[] SupportedGames { get; private set; } = [];
|
||||||
private static readonly string[] DisabledCommands = {nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"};
|
private static readonly string[] DisabledCommands = [nameof(PrivateMessageAdminsCommand), "PrivateMessageCommand"];
|
||||||
private readonly IInteractionRegistration _interactionRegistration;
|
private readonly IInteractionRegistration _interactionRegistration;
|
||||||
private readonly IRemoteCommandService _remoteCommandService;
|
private readonly IRemoteCommandService _remoteCommandService;
|
||||||
private readonly MuteManager _muteManager;
|
private readonly MuteManager _muteManager;
|
||||||
private const string MuteInteraction = "Webfront::Profile::Mute";
|
private const string MuteInteraction = "Webfront::Profile::Mute";
|
||||||
|
|
||||||
public Plugin(IInteractionRegistration interactionRegistration,
|
public Plugin(IInteractionRegistration interactionRegistration, IRemoteCommandService remoteCommandService, MuteManager muteManager)
|
||||||
IRemoteCommandService remoteCommandService, MuteManager muteManager)
|
|
||||||
{
|
{
|
||||||
_interactionRegistration = interactionRegistration;
|
_interactionRegistration = interactionRegistration;
|
||||||
_remoteCommandService = remoteCommandService;
|
_remoteCommandService = remoteCommandService;
|
||||||
@ -72,7 +71,7 @@ public class Plugin : IPluginV2
|
|||||||
return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast;
|
return !DisabledCommands.Contains(command.GetType().Name) && !command.IsBroadcast;
|
||||||
});
|
});
|
||||||
|
|
||||||
_interactionRegistration.RegisterInteraction(MuteInteraction, async (targetClientId, game, token) =>
|
_interactionRegistration.RegisterInteraction(MuteInteraction, async (targetClientId, game, _) =>
|
||||||
{
|
{
|
||||||
if (!targetClientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game)game.Value))
|
if (!targetClientId.HasValue || game.HasValue && !SupportedGames.Contains((Server.Game)game.Value))
|
||||||
{
|
{
|
||||||
@ -84,12 +83,12 @@ public class Plugin : IPluginV2
|
|||||||
.MuteState;
|
.MuteState;
|
||||||
var server = manager.GetServers().First();
|
var server = manager.GetServers().First();
|
||||||
|
|
||||||
string GetCommandName(Type commandType) =>
|
|
||||||
manager.Commands.FirstOrDefault(command => command.GetType() == commandType)?.Name ?? "";
|
|
||||||
|
|
||||||
return clientMuteMetaState is MuteState.Unmuted or MuteState.Unmuting
|
return clientMuteMetaState is MuteState.Unmuted or MuteState.Unmuting
|
||||||
? CreateMuteInteraction(targetClientId.Value, server, GetCommandName)
|
? CreateMuteInteraction(targetClientId.Value, server, GetCommandName)
|
||||||
: CreateUnmuteInteraction(targetClientId.Value, server, GetCommandName);
|
: CreateUnmuteInteraction(targetClientId.Value, server, GetCommandName);
|
||||||
|
|
||||||
|
string GetCommandName(Type commandType) =>
|
||||||
|
manager.Commands.FirstOrDefault(command => command.GetType() == commandType)?.Name ?? string.Empty;
|
||||||
});
|
});
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
@ -108,9 +107,9 @@ public class Plugin : IPluginV2
|
|||||||
}
|
}
|
||||||
|
|
||||||
var networkIds = updateEvent.Clients.Select(client => client.NetworkId).ToList();
|
var networkIds = updateEvent.Clients.Select(client => client.NetworkId).ToList();
|
||||||
var ingameClients = updateEvent.Server.ConnectedClients.Where(client => networkIds.Contains(client.NetworkId));
|
var inGameClients = updateEvent.Server.ConnectedClients.Where(client => networkIds.Contains(client.NetworkId));
|
||||||
|
|
||||||
await Task.WhenAll(ingameClients.Select(async client =>
|
await Task.WhenAll(inGameClients.Select(async client =>
|
||||||
{
|
{
|
||||||
var muteMetaUpdate = await _muteManager.GetCurrentMuteState(client);
|
var muteMetaUpdate = await _muteManager.GetCurrentMuteState(client);
|
||||||
if (!muteMetaUpdate.CommandExecuted)
|
if (!muteMetaUpdate.CommandExecuted)
|
||||||
@ -136,7 +135,7 @@ public class Plugin : IPluginV2
|
|||||||
{
|
{
|
||||||
var muteMetaSay = await _muteManager.GetCurrentMuteState(messageEvent.Origin);
|
var muteMetaSay = await _muteManager.GetCurrentMuteState(messageEvent.Origin);
|
||||||
|
|
||||||
if (muteMetaSay.MuteState == MuteState.Muted)
|
if (muteMetaSay.MuteState is MuteState.Muted)
|
||||||
{
|
{
|
||||||
// Let the client know when their mute expires.
|
// Let the client know when their mute expires.
|
||||||
messageEvent.Origin.Tell(Utilities.CurrentLocalization
|
messageEvent.Origin.Tell(Utilities.CurrentLocalization
|
||||||
@ -190,6 +189,29 @@ public class Plugin : IPluginV2
|
|||||||
Values = (Dictionary<string, string>?)null
|
Values = (Dictionary<string, string>?)null
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var presetReasonInput = new
|
||||||
|
{
|
||||||
|
Name = "PresetReason",
|
||||||
|
Label = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_ACTION_LABEL_PRESET_REASON"],
|
||||||
|
Type = "select",
|
||||||
|
Values = (Dictionary<string, string>?)new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ string.Empty, string.Empty },
|
||||||
|
{
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_ABUSIVE"],
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_ABUSIVE"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_SPAMMING"],
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_SPAMMING"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_OTHER"],
|
||||||
|
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_MUTE_REASON_OTHER"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var durationInput = new
|
var durationInput = new
|
||||||
{
|
{
|
||||||
Name = "Duration",
|
Name = "Duration",
|
||||||
@ -206,7 +228,7 @@ public class Plugin : IPluginV2
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var inputs = new[] {reasonInput, durationInput};
|
var inputs = new[] { reasonInput, presetReasonInput, durationInput };
|
||||||
var inputsJson = JsonSerializer.Serialize(inputs);
|
var inputsJson = JsonSerializer.Serialize(inputs);
|
||||||
|
|
||||||
return new InteractionData
|
return new InteractionData
|
||||||
@ -215,7 +237,7 @@ public class Plugin : IPluginV2
|
|||||||
Name = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"],
|
Name = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_MUTE"],
|
||||||
DisplayMeta = "oi-volume-off",
|
DisplayMeta = "oi-volume-off",
|
||||||
ActionPath = "DynamicAction",
|
ActionPath = "DynamicAction",
|
||||||
ActionMeta = new()
|
ActionMeta = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "InteractionId", MuteInteraction },
|
{ "InteractionId", MuteInteraction },
|
||||||
{ "Inputs", inputsJson },
|
{ "Inputs", inputsJson },
|
||||||
@ -249,11 +271,14 @@ public class Plugin : IPluginV2
|
|||||||
args.Add(duration);
|
args.Add(duration);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.TryGetValue(reasonInput.Name, out var reason))
|
var definedReason = meta.TryGetValue(reasonInput.Name, out var reason) ? reason : string.Empty;
|
||||||
|
if (meta.TryGetValue(presetReasonInput.Name, out var presetReason) && string.IsNullOrWhiteSpace(definedReason))
|
||||||
{
|
{
|
||||||
args.Add(reason);
|
definedReason = presetReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
args.Add(definedReason);
|
||||||
|
|
||||||
var commandResponse =
|
var commandResponse =
|
||||||
await _remoteCommandService.Execute(originId, targetId, muteCommand, args, server);
|
await _remoteCommandService.Execute(originId, targetId, muteCommand, args, server);
|
||||||
return string.Join(".", commandResponse.Select(result => result.Response));
|
return string.Join(".", commandResponse.Select(result => result.Response));
|
||||||
@ -277,11 +302,10 @@ public class Plugin : IPluginV2
|
|||||||
return new InteractionData
|
return new InteractionData
|
||||||
{
|
{
|
||||||
EntityId = targetClientId,
|
EntityId = targetClientId,
|
||||||
Name = Utilities.CurrentLocalization.LocalizationIndex[
|
Name = Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"],
|
||||||
"WEBFRONT_PROFILE_CONTEXT_MENU_ACTION_UNMUTE"],
|
|
||||||
DisplayMeta = "oi-volume-high",
|
DisplayMeta = "oi-volume-high",
|
||||||
ActionPath = "DynamicAction",
|
ActionPath = "DynamicAction",
|
||||||
ActionMeta = new()
|
ActionMeta = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
{ "InteractionId", MuteInteraction },
|
{ "InteractionId", MuteInteraction },
|
||||||
{ "Outputs", reasonInput.Name },
|
{ "Outputs", reasonInput.Name },
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.ProfanityDeterment</PackageId>
|
||||||
@ -16,11 +16,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -233,7 +233,7 @@ const plugin = {
|
|||||||
|
|
||||||
// todo: refactor to mapping if possible
|
// todo: refactor to mapping if possible
|
||||||
if (event.eventType === 'ClientDataRequested') {
|
if (event.eventType === 'ClientDataRequested') {
|
||||||
const client = server.getClientByNumber(event.clientNumber);
|
const client = server.connectedClients[event.clientNumber];
|
||||||
|
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
this.logger.logDebug('Found client {name}', client.name);
|
this.logger.logDebug('Found client {name}', client.name);
|
||||||
@ -269,8 +269,9 @@ const plugin = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _;
|
||||||
if (event.eventType === 'SetClientDataRequested') {
|
if (event.eventType === 'SetClientDataRequested') {
|
||||||
let client = server.getClientByNumber(event.clientNumber);
|
let client = server.connectedClients[event.clientNumber];
|
||||||
let clientId;
|
let clientId;
|
||||||
|
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
@ -298,12 +299,12 @@ const plugin = {
|
|||||||
const parsedValue = parseInt(event.data['value']);
|
const parsedValue = parseInt(event.data['value']);
|
||||||
const key = event.data['key'].toString();
|
const key = event.data['key'].toString();
|
||||||
if (!isNaN(parsedValue)) {
|
if (!isNaN(parsedValue)) {
|
||||||
event.data['direction'] = 'up' ?
|
_ = event.data['direction'] === 'increment' ?
|
||||||
(await metaService.incrementPersistentMeta(key, parsedValue, clientId, token)).result :
|
(await metaService.incrementPersistentMeta(key, parsedValue, clientId, token)).result :
|
||||||
(await metaService.decrementPersistentMeta(key, parsedValue, clientId, token)).result;
|
(await metaService.decrementPersistentMeta(key, parsedValue, clientId, token)).result;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const _ = (await metaService.setPersistentMeta(event.data['key'], event.data['value'], clientId, token)).result;
|
_ = (await metaService.setPersistentMeta(event.data['key'], event.data['value'], clientId, token)).result;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.data['key'] === 'PersistentClientGuid') {
|
if (event.data['key'] === 'PersistentClientGuid') {
|
||||||
|
@ -16,7 +16,7 @@ var plugin = {
|
|||||||
rconParser.Configuration.CommandPrefixes.Kick = 'kickClient {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.Kick = 'kickClient {0} "{1}"';
|
||||||
rconParser.Configuration.CommandPrefixes.Ban = 'kickClient {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.Ban = 'kickClient {0} "{1}"';
|
||||||
rconParser.Configuration.CommandPrefixes.TempBan = 'kickClient {0} "{1}"';
|
rconParser.Configuration.CommandPrefixes.TempBan = 'kickClient {0} "{1}"';
|
||||||
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint';
|
rconParser.Configuration.CommandPrefixes.RConResponse = '\xff\xff\xff\xffprint\n';
|
||||||
rconParser.Configuration.Dvar.Pattern = '^ *\\"(.+)\\" is: \\"(.+)?\\" default: \\"(.+)?\\"\\n?(?:latched: \\"(.+)?\\"\\n?)?(.*)$';
|
rconParser.Configuration.Dvar.Pattern = '^ *\\"(.+)\\" is: \\"(.+)?\\" default: \\"(.+)?\\"\\n?(?:latched: \\"(.+)?\\"\\n?)?(.*)$';
|
||||||
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
rconParser.Configuration.Status.Pattern = '^ *([0-9]+) +-?([0-9]+) +(Yes|No) +((?:[A-Z]+|[0-9]+)) +((?:[a-z]|[0-9]){8,32}|(?:[a-z]|[0-9]){8,32}|bot[0-9]+|(?:[0-9]+)) *(.{0,32}) +(\\d+\\.\\d+\\.\\d+.\\d+\\:-*\\d{1,5}|0+.0+:-*\\d{1,5}|loopback|unknown|bot) +(-*[0-9]+) *$';
|
||||||
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
rconParser.Configuration.StatusHeader.Pattern = 'num +score +bot +ping +guid +name +address +qport *';
|
||||||
|
@ -9,7 +9,7 @@ const serverOrderCache = [];
|
|||||||
|
|
||||||
const plugin = {
|
const plugin = {
|
||||||
author: 'RaidMax',
|
author: 'RaidMax',
|
||||||
version: '1.0',
|
version: '1.1',
|
||||||
name: 'Server Banner',
|
name: 'Server Banner',
|
||||||
serviceResolver: null,
|
serviceResolver: null,
|
||||||
scriptHelper: null,
|
scriptHelper: null,
|
||||||
@ -26,6 +26,9 @@ const plugin = {
|
|||||||
this.manager = serviceResolver.resolveService('IManager');
|
this.manager = serviceResolver.resolveService('IManager');
|
||||||
this.logger = serviceResolver.resolveService('ILogger', ['ScriptPluginV2']);
|
this.logger = serviceResolver.resolveService('ILogger', ['ScriptPluginV2']);
|
||||||
this.webfrontUrl = serviceResolver.resolveService('ApplicationConfiguration').webfrontUrl;
|
this.webfrontUrl = serviceResolver.resolveService('ApplicationConfiguration').webfrontUrl;
|
||||||
|
|
||||||
|
this.logger.logInformation('{Name} {Version} by {Author} loaded,', this.name, this.version,
|
||||||
|
this.author);
|
||||||
},
|
},
|
||||||
|
|
||||||
onServerMonitoringStart: function (startEvent) {
|
onServerMonitoringStart: function (startEvent) {
|
||||||
@ -123,11 +126,13 @@ const plugin = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
plugin.manager.getServers().forEach(eachServer => {
|
const servers = plugin.manager.servers;
|
||||||
if (eachServer.id === serverId) {
|
for (let i = 0; i < servers.length; i++) {
|
||||||
server = eachServer;
|
if (servers[i].id === serverId) {
|
||||||
|
server = servers[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (serverLocationCache[server.listenAddress] === undefined) {
|
if (serverLocationCache[server.listenAddress] === undefined) {
|
||||||
plugin.onServerMonitoringStart({
|
plugin.onServerMonitoringStart({
|
||||||
@ -280,7 +285,7 @@ const plugin = {
|
|||||||
<div class="server-container small" id="server">
|
<div class="server-container small" id="server">
|
||||||
<div class="first-line small">
|
<div class="first-line small">
|
||||||
<div class="game-icon small"></div>
|
<div class="game-icon small"></div>
|
||||||
<div class="header" style="${colorLeft}">${server.serverName.stripColors()}</div>
|
<div class="header" id="serverName" style="${colorLeft}"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="third-line game-info small">
|
<div class="third-line game-info small">
|
||||||
${status}
|
${status}
|
||||||
@ -298,6 +303,10 @@ const plugin = {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
const serverNameElem = document.getElementById('serverName');
|
||||||
|
serverNameElem.textContent = '${server.serverName.stripColors()}';
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
}
|
||||||
@ -310,7 +319,7 @@ const plugin = {
|
|||||||
style="background: url('https://raidmax.org/resources/images/icons/games/${gameCode}.jpg');">
|
style="background: url('https://raidmax.org/resources/images/icons/games/${gameCode}.jpg');">
|
||||||
</div>
|
</div>
|
||||||
<div style="flex: 1; ${colorLeft}" class="game-info large">
|
<div style="flex: 1; ${colorLeft}" class="game-info large">
|
||||||
<div class="header">${server.serverName.stripColors()}</div>
|
<div class="header" id="serverName"></div>
|
||||||
<div class="text-weight-lighter subtitle">${displayIp}:${server.listenPort}</div>
|
<div class="text-weight-lighter subtitle">${displayIp}:${server.listenPort}</div>
|
||||||
<div class="players-flag-section">
|
<div class="players-flag-section">
|
||||||
<div class="subtitle">${server.throttled ? '-' : server.clientNum}/${server.maxClients} Players</div>
|
<div class="subtitle">${server.throttled ? '-' : server.clientNum}/${server.maxClients} Players</div>
|
||||||
@ -324,6 +333,10 @@ const plugin = {
|
|||||||
${status}
|
${status}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script>
|
||||||
|
const serverNameElem = document.getElementById('serverName');
|
||||||
|
serverNameElem.textContent = '${server.serverName.stripColors()}';
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
};
|
};
|
||||||
@ -346,22 +359,24 @@ const plugin = {
|
|||||||
|
|
||||||
interactionData.scriptAction = (_, __, ___, ____, _____) => {
|
interactionData.scriptAction = (_, __, ___, ____, _____) => {
|
||||||
if (Object.keys(serverOrderCache).length === 0) {
|
if (Object.keys(serverOrderCache).length === 0) {
|
||||||
plugin.manager.getServers().forEach(server => {
|
for (let i = 0; i < plugin.manager.servers.length; i++) {
|
||||||
|
const server = plugin.manager.servers[i];
|
||||||
plugin.onServerMonitoringStart({
|
plugin.onServerMonitoringStart({
|
||||||
server: server
|
server: server
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = '<div class="d-flex flex-row flex-wrap" style="margin-left: -1rem; margin-top: -1rem;">';
|
let response = '<div class="d-flex flex-row flex-wrap" style="margin-left: -1rem; margin-top: -1rem;">';
|
||||||
Object.keys(serverOrderCache).forEach(key => {
|
Object.keys(serverOrderCache).forEach(key => {
|
||||||
const servers = serverOrderCache[key];
|
const servers = serverOrderCache[key];
|
||||||
servers.forEach(eachServer => {
|
for (let i = 0; i < servers.length; i++) {
|
||||||
|
const eachServer = servers[i];
|
||||||
response += `<div class="w-full w-xl-half">
|
response += `<div class="w-full w-xl-half">
|
||||||
<div class="card m-10 p-20">
|
<div class="card m-10 p-20">
|
||||||
<div class="font-size-16 mb-10">
|
<div class="font-size-16 mb-10">
|
||||||
<div class="badge ml-10 float-right font-size-16">${eachServer.gameCode}</div>
|
<div class="badge ml-10 float-right font-size-16">${eachServer.gameCode}</div>
|
||||||
${eachServer.serverName.stripColors()}
|
<div id="serverName"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="overflow: hidden">
|
<div style="overflow: hidden">
|
||||||
@ -387,8 +402,12 @@ const plugin = {
|
|||||||
<br/> width="400" height="70" style="border-width: 0; overflow: hidden;"><br/>
|
<br/> width="400" height="70" style="border-width: 0; overflow: hidden;"><br/>
|
||||||
</iframe></div>
|
</iframe></div>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>
|
||||||
});
|
<script>
|
||||||
|
const serverNameElem = document.getElementById('serverName');
|
||||||
|
serverNameElem.textContent = '${eachServer.serverName.stripColors()}';
|
||||||
|
</script>`;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
response += '</div>';
|
response += '</div>';
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.Stats</PackageId>
|
||||||
@ -17,11 +17,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -6,8 +6,8 @@ using SharedLibraryCore.Database.Models;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Data.Abstractions;
|
using Data.Abstractions;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
@ -108,8 +108,9 @@ public class Plugin : IPluginV2
|
|||||||
var response =
|
var response =
|
||||||
await wc.GetStringAsync(new Uri(
|
await wc.GetStringAsync(new Uri(
|
||||||
$"http://ip-api.com/json/{ip}?lang={Utilities.CurrentLocalization.LocalizationName.Split("-").First().ToLower()}"));
|
$"http://ip-api.com/json/{ip}?lang={Utilities.CurrentLocalization.LocalizationName.Split("-").First().ToLower()}"));
|
||||||
var responseObj = JObject.Parse(response);
|
|
||||||
response = responseObj["country"]?.ToString();
|
var json = JsonDocument.Parse(response);
|
||||||
|
response = json.RootElement.TryGetProperty("country", out var countryElement) ? countryElement.GetString() : null;
|
||||||
|
|
||||||
return string.IsNullOrEmpty(response)
|
return string.IsNullOrEmpty(response)
|
||||||
? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"]
|
? Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_WELCOME_UNKNOWN_COUNTRY"]
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ApplicationIcon />
|
<ApplicationIcon />
|
||||||
<StartupObject />
|
<StartupObject />
|
||||||
<PackageId>RaidMax.IW4MAdmin.Plugins.Welcome</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.Plugins.Welcome</PackageId>
|
||||||
@ -15,12 +15,8 @@
|
|||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
|
||||||
<Exec Command="dotnet publish $(ProjectPath) -c $(ConfigurationName) -o $(ProjectDir)..\..\Build\Plugins --no-build --no-restore --no-dependencies" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.2.5.3" PrivateAssets="All" />
|
<PackageReference Include="RaidMax.IW4MAdmin.SharedLibraryCore" Version="2024.6.22.1" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Data.Models.Misc;
|
using Data.Models.Misc;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using SharedLibraryCore.Configuration.Attributes;
|
using SharedLibraryCore.Configuration.Attributes;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
using static Data.Models.Client.EFClient;
|
using static Data.Models.Client.EFClient;
|
||||||
@ -22,7 +22,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
public bool EnableWebFront { get; set; }
|
public bool EnableWebFront { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BIND_URL")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BIND_URL")]
|
||||||
public string WebfrontBindUrl { get; set; }
|
public string WebfrontBindUrl { get; set; } = "http://0.0.0.0:1624";
|
||||||
|
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MANUAL_URL")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MANUAL_URL")]
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using System.Text.Json.Serialization;
|
||||||
using Newtonsoft.Json.Converters;
|
using SharedLibraryCore.Helpers;
|
||||||
using static Data.Models.Client.EFClient;
|
using static Data.Models.Client.EFClient;
|
||||||
using static SharedLibraryCore.Server;
|
using static SharedLibraryCore.Server;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Configuration
|
namespace SharedLibraryCore.Configuration;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Config driven command properties
|
/// Config driven command properties
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -24,7 +24,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the minimum permission level needed to execute the
|
/// Specifies the minimum permission level needed to execute the
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonConverter(typeof(StringEnumConverter))]
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
||||||
public Permission MinimumPermission { get; set; }
|
public Permission MinimumPermission { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -35,7 +35,6 @@ namespace SharedLibraryCore.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the games supporting the functionality of the command
|
/// Specifies the games supporting the functionality of the command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonProperty(ItemConverterType = typeof(StringEnumConverter))]
|
[JsonConverter(typeof(GameArrayJsonConverter))]
|
||||||
public Game[] SupportedGames { get; set; } = Array.Empty<Game>();
|
public Game[] SupportedGames { get; set; } = Array.Empty<Game>();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@ -9,16 +9,9 @@ namespace SharedLibraryCore.Configuration
|
|||||||
{
|
{
|
||||||
public class ServerConfiguration : IBaseConfiguration
|
public class ServerConfiguration : IBaseConfiguration
|
||||||
{
|
{
|
||||||
private readonly IList<IRConParser> _rconParsers;
|
private readonly IList<IRConParser> _rconParsers = new List<IRConParser>();
|
||||||
private IRConParser _selectedParser;
|
private IRConParser _selectedParser;
|
||||||
|
|
||||||
public ServerConfiguration()
|
|
||||||
{
|
|
||||||
_rconParsers = new List<IRConParser>();
|
|
||||||
Rules = new string[0];
|
|
||||||
AutoMessages = new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_IP")]
|
||||||
public string IPAddress { get; set; }
|
public string IPAddress { get; set; }
|
||||||
|
|
||||||
@ -29,10 +22,10 @@ namespace SharedLibraryCore.Configuration
|
|||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_RULES")]
|
||||||
public string[] Rules { get; set; } = new string[0];
|
public string[] Rules { get; set; } = [];
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_AUTO_MESSAGES")]
|
||||||
public string[] AutoMessages { get; set; } = new string[0];
|
public string[] AutoMessages { get; set; } = [];
|
||||||
|
|
||||||
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")]
|
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_SERVER_PATH")]
|
||||||
[ConfigurationOptional]
|
[ConfigurationOptional]
|
||||||
@ -88,7 +81,7 @@ namespace SharedLibraryCore.Configuration
|
|||||||
var passwords = _selectedParser.TryGetRConPasswords();
|
var passwords = _selectedParser.TryGetRConPasswords();
|
||||||
if (passwords.Length > 1)
|
if (passwords.Length > 1)
|
||||||
{
|
{
|
||||||
var (index, value) =
|
var (index, _) =
|
||||||
loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection(loc["SETUP_RCON_PASSWORD_MANUAL"], null,
|
loc["SETUP_RCON_PASSWORD_PROMPT"].PromptSelection(loc["SETUP_RCON_PASSWORD_MANUAL"], null,
|
||||||
passwords.Select(pw =>
|
passwords.Select(pw =>
|
||||||
$"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}")
|
$"{pw.Item1}{(string.IsNullOrEmpty(pw.Item2) ? "" : " " + pw.Item2)}")
|
||||||
@ -113,9 +106,8 @@ namespace SharedLibraryCore.Configuration
|
|||||||
Password = loc["SETUP_SERVER_RCON"].PromptString();
|
Password = loc["SETUP_SERVER_RCON"].PromptString();
|
||||||
}
|
}
|
||||||
|
|
||||||
AutoMessages = new string[0];
|
AutoMessages = [];
|
||||||
Rules = new string[0];
|
Rules = [];
|
||||||
ManualLogPath = null;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ public abstract class CoreEvent
|
|||||||
{
|
{
|
||||||
public Guid Id { get; } = Guid.NewGuid();
|
public Guid Id { get; } = Guid.NewGuid();
|
||||||
public Guid? CorrelationId { get; init; }
|
public Guid? CorrelationId { get; init; }
|
||||||
public object Source { get; init; }
|
public object Source { get; set; }
|
||||||
public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
|
public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
|
||||||
public DateTimeOffset? ProcessedAt { get; set; }
|
public DateTimeOffset? ProcessedAt { get; set; }
|
||||||
}
|
}
|
||||||
|
6
SharedLibraryCore/Events/Game/GameLogEvent.cs
Normal file
6
SharedLibraryCore/Events/Game/GameLogEvent.cs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
namespace SharedLibraryCore.Events.Game;
|
||||||
|
|
||||||
|
public class GameLogEvent : GameEventV2
|
||||||
|
{
|
||||||
|
public string LogLine { get; set; }
|
||||||
|
}
|
@ -1,6 +1,77 @@
|
|||||||
namespace SharedLibraryCore.Events.Game;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using SharedLibraryCore.Interfaces.Events;
|
||||||
|
|
||||||
public class GameScriptEvent : GameEventV2
|
namespace SharedLibraryCore.Events.Game;
|
||||||
|
|
||||||
|
public class GameScriptEvent : GameEventV2, IGameScriptEvent
|
||||||
{
|
{
|
||||||
public string ScriptData { get; init; }
|
public string ScriptData { get; set; }
|
||||||
|
public string EventName { get; } = null;
|
||||||
|
|
||||||
|
public virtual void ParseArguments()
|
||||||
|
{
|
||||||
|
var arguments = ScriptData.Split(';', StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
var propIndex = 0;
|
||||||
|
foreach (var argument in arguments)
|
||||||
|
{
|
||||||
|
var parts = argument.Split(['='], 2);
|
||||||
|
PropertyInfo propertyInfo = null;
|
||||||
|
string rawValue;
|
||||||
|
|
||||||
|
if (parts.Length == 2) // handle as key/value pairs
|
||||||
|
{
|
||||||
|
var propertyName = parts[0].Trim();
|
||||||
|
rawValue = parts[1].Trim();
|
||||||
|
propertyInfo = GetType().GetProperty(propertyName,
|
||||||
|
BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.DeclaredOnly);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rawValue = argument;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
propertyInfo =
|
||||||
|
GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase |
|
||||||
|
BindingFlags.DeclaredOnly)[
|
||||||
|
propIndex];
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (propertyInfo is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var method = propertyInfo.PropertyType.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public,
|
||||||
|
[typeof(string)]);
|
||||||
|
|
||||||
|
var convertedValue = method is not null
|
||||||
|
? method!.Invoke(null, [rawValue])!
|
||||||
|
: Convert.ChangeType(rawValue, propertyInfo.PropertyType);
|
||||||
|
|
||||||
|
propertyInfo.SetValue(this, convertedValue);
|
||||||
|
}
|
||||||
|
catch (TargetInvocationException ex) when (ex.InnerException is FormatException &&
|
||||||
|
propertyInfo.PropertyType == typeof(bool))
|
||||||
|
{
|
||||||
|
propertyInfo.SetValue(this, rawValue != "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
propIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using Data.Models;
|
using Data.Models;
|
||||||
using Data.Models.Client;
|
using SharedLibraryCore.Database.Models;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Events.Management;
|
namespace SharedLibraryCore.Events.Management;
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Events.Server;
|
||||||
|
|
||||||
|
public class ServerStatusReceiveEvent : GameServerEvent
|
||||||
|
{
|
||||||
|
public IStatusResponse Response { get; set; }
|
||||||
|
public string RawData { get; set; }
|
||||||
|
}
|
@ -1,27 +1,58 @@
|
|||||||
using System;
|
using System;
|
||||||
using Newtonsoft.Json;
|
using System.Collections.Generic;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace SharedLibraryCore.Helpers;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Helpers
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// JSON converter for the build number
|
/// JSON converter for the build number
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class BuildNumberJsonConverter : JsonConverter
|
public class BuildNumberJsonConverter : JsonConverter<BuildNumber>
|
||||||
{
|
{
|
||||||
public override bool CanConvert(Type objectType)
|
public override BuildNumber Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
return objectType == typeof(string);
|
var stringValue = reader.GetString();
|
||||||
|
return BuildNumber.Parse(stringValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
public override void Write(Utf8JsonWriter writer, BuildNumber value, JsonSerializerOptions options)
|
||||||
JsonSerializer serializer)
|
|
||||||
{
|
{
|
||||||
return BuildNumber.Parse(reader.Value.ToString());
|
writer.WriteStringValue(value.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
public class GameArrayJsonConverter : JsonConverter<Server.Game[]>
|
||||||
{
|
{
|
||||||
writer.WriteValue(value.ToString());
|
public override Server.Game[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
}
|
{
|
||||||
|
List<Server.Game> games = [];
|
||||||
|
|
||||||
|
while (reader.Read())
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonTokenType.EndArray)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gameString = reader.GetString();
|
||||||
|
var game = Enum.Parse<Server.Game>(gameString);
|
||||||
|
games.Add(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
return games.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, Server.Game[] value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
writer.WriteStartArray();
|
||||||
|
|
||||||
|
foreach (var game in value)
|
||||||
|
{
|
||||||
|
writer.WriteStringValue(game.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteEndArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,6 +86,11 @@ public interface IGameEventSubscriptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static event Func<GameScriptEvent, CancellationToken, Task> ScriptEventTriggered;
|
static event Func<GameScriptEvent, CancellationToken, Task> ScriptEventTriggered;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when game log prints a line that is not handled by any other cases
|
||||||
|
/// </summary>
|
||||||
|
static event Func<GameLogEvent, CancellationToken, Task> GameLogEventTriggered;
|
||||||
|
|
||||||
static Task InvokeEventAsync(CoreEvent coreEvent, CancellationToken token)
|
static Task InvokeEventAsync(CoreEvent coreEvent, CancellationToken token)
|
||||||
{
|
{
|
||||||
return coreEvent switch
|
return coreEvent switch
|
||||||
@ -100,6 +105,7 @@ public interface IGameEventSubscriptions
|
|||||||
ClientCommandEvent clientCommandEvent => ClientEnteredCommand?.InvokeAsync(clientCommandEvent, token) ?? Task.CompletedTask,
|
ClientCommandEvent clientCommandEvent => ClientEnteredCommand?.InvokeAsync(clientCommandEvent, token) ?? Task.CompletedTask,
|
||||||
ClientMessageEvent clientMessageEvent => ClientMessaged?.InvokeAsync(clientMessageEvent, token) ?? Task.CompletedTask,
|
ClientMessageEvent clientMessageEvent => ClientMessaged?.InvokeAsync(clientMessageEvent, token) ?? Task.CompletedTask,
|
||||||
GameScriptEvent gameScriptEvent => ScriptEventTriggered?.InvokeAsync(gameScriptEvent, token) ?? Task.CompletedTask,
|
GameScriptEvent gameScriptEvent => ScriptEventTriggered?.InvokeAsync(gameScriptEvent, token) ?? Task.CompletedTask,
|
||||||
|
GameLogEvent gameLogEvent => GameLogEventTriggered?.InvokeAsync(gameLogEvent, token) ?? Task.CompletedTask,
|
||||||
_ => Task.CompletedTask
|
_ => Task.CompletedTask
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -116,5 +122,6 @@ public interface IGameEventSubscriptions
|
|||||||
ClientMessaged = null;
|
ClientMessaged = null;
|
||||||
ClientEnteredCommand = null;
|
ClientEnteredCommand = null;
|
||||||
ScriptEventTriggered = null;
|
ScriptEventTriggered = null;
|
||||||
|
GameLogEventTriggered = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
SharedLibraryCore/Interfaces/Events/IGameScriptEvent.cs
Normal file
8
SharedLibraryCore/Interfaces/Events/IGameScriptEvent.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace SharedLibraryCore.Interfaces.Events;
|
||||||
|
|
||||||
|
public interface IGameScriptEvent
|
||||||
|
{
|
||||||
|
string ScriptData { get; set; }
|
||||||
|
string EventName { get; }
|
||||||
|
void ParseArguments();
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
namespace SharedLibraryCore.Interfaces.Events;
|
||||||
|
|
||||||
|
public interface IGameScriptEventFactory
|
||||||
|
{
|
||||||
|
IGameScriptEvent Create(string eventType, string logData);
|
||||||
|
}
|
@ -73,6 +73,11 @@ public interface IGameServerEventSubscriptions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
static event Func<ServerValueSetCompleteEvent, CancellationToken, Task> ServerValueSetCompleted;
|
static event Func<ServerValueSetCompleteEvent, CancellationToken, Task> ServerValueSetCompleted;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a server's status response is received
|
||||||
|
/// </summary>
|
||||||
|
static event Func<ServerStatusReceiveEvent, CancellationToken, Task> ServerStatusReceived;
|
||||||
|
|
||||||
static Task InvokeEventAsync(CoreEvent coreEvent, CancellationToken token)
|
static Task InvokeEventAsync(CoreEvent coreEvent, CancellationToken token)
|
||||||
{
|
{
|
||||||
return coreEvent switch
|
return coreEvent switch
|
||||||
@ -88,6 +93,7 @@ public interface IGameServerEventSubscriptions
|
|||||||
ServerValueReceiveEvent serverValueReceiveEvent => ServerValueReceived?.InvokeAsync(serverValueReceiveEvent, token) ?? Task.CompletedTask,
|
ServerValueReceiveEvent serverValueReceiveEvent => ServerValueReceived?.InvokeAsync(serverValueReceiveEvent, token) ?? Task.CompletedTask,
|
||||||
ServerValueSetRequestEvent serverValueSetRequestEvent => ServerValueSetRequested?.InvokeAsync(serverValueSetRequestEvent, token) ?? Task.CompletedTask,
|
ServerValueSetRequestEvent serverValueSetRequestEvent => ServerValueSetRequested?.InvokeAsync(serverValueSetRequestEvent, token) ?? Task.CompletedTask,
|
||||||
ServerValueSetCompleteEvent serverValueSetCompleteEvent => ServerValueSetCompleted?.InvokeAsync(serverValueSetCompleteEvent, token) ?? Task.CompletedTask,
|
ServerValueSetCompleteEvent serverValueSetCompleteEvent => ServerValueSetCompleted?.InvokeAsync(serverValueSetCompleteEvent, token) ?? Task.CompletedTask,
|
||||||
|
ServerStatusReceiveEvent serverStatusReceiveEvent => ServerStatusReceived?.InvokeAsync(serverStatusReceiveEvent, token) ?? Task.CompletedTask,
|
||||||
_ => Task.CompletedTask
|
_ => Task.CompletedTask
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Net;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Data.Models;
|
using Data.Models;
|
||||||
@ -19,6 +20,9 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task Kick(string reason, EFClient target, EFClient origin, EFPenalty previousPenalty = null);
|
Task Kick(string reason, EFClient target, EFClient origin, EFPenalty previousPenalty = null);
|
||||||
|
|
||||||
|
IPEndPoint ResolvedIpEndPoint { get; }
|
||||||
|
IRConParser RconParser { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute a server command
|
/// Execute a server command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -41,6 +41,7 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
ILogger GetLogger(long serverId);
|
ILogger GetLogger(long serverId);
|
||||||
|
|
||||||
IList<Server> GetServers();
|
IList<Server> GetServers();
|
||||||
|
List<Server> Servers { get; }
|
||||||
IList<IManagerCommand> GetCommands();
|
IList<IManagerCommand> GetCommands();
|
||||||
IList<MessageToken> GetMessageTokens();
|
IList<MessageToken> GetMessageTokens();
|
||||||
IList<EFClient> GetActiveClients();
|
IList<EFClient> GetActiveClients();
|
||||||
|
@ -15,35 +15,35 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores the game/client specific version (usually the value of the "version" DVAR)
|
/// stores the game/client specific version (usually the value of the "version" DVAR)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Version { get; }
|
string Version { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...)
|
/// specifies the game name (usually the internal studio iteration ie: IW4, T5 etc...)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Game GameName { get; }
|
Game GameName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates if the game supports generating a log path from DVAR retrieval
|
/// indicates if the game supports generating a log path from DVAR retrieval
|
||||||
/// of fs_game, fs_basepath, g_log
|
/// of fs_game, fs_basepath, g_log
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool CanGenerateLogPath { get; }
|
bool CanGenerateLogPath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies the name of the parser
|
/// specifies the name of the parser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string Name { get; }
|
string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies the type of rcon engine
|
/// specifies the type of rcon engine
|
||||||
/// eg: COD, Source
|
/// eg: COD, Source
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string RConEngine { get; }
|
string RConEngine { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates that the game does not log to the mods folder (when mod is loaded),
|
/// indicates that the game does not log to the mods folder (when mod is loaded),
|
||||||
/// but rather always to the fs_basegame directory
|
/// but rather always to the fs_basegame directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsOneLog { get; }
|
bool IsOneLog { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// retrieves the value of a given DVAR
|
/// retrieves the value of a given DVAR
|
||||||
@ -54,7 +54,8 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <param name="fallbackValue">default value to return if dvar retrieval fails</param>
|
/// <param name="fallbackValue">default value to return if dvar retrieval fails</param>
|
||||||
/// <param name="token"></param>
|
/// <param name="token"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default, CancellationToken token = default);
|
Task<Dvar<T>> GetDvarAsync<T>(IRConConnection connection, string dvarName, T fallbackValue = default,
|
||||||
|
CancellationToken token = default);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// set value of DVAR by name
|
/// set value of DVAR by name
|
||||||
|
@ -10,74 +10,74 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores the command format for console commands
|
/// stores the command format for console commands
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CommandPrefix CommandPrefixes { get; }
|
CommandPrefix CommandPrefixes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores the regex info for parsing get status response
|
/// stores the regex info for parsing get status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex Status { get; }
|
ParserRegex Status { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores regex info for parsing the map line from rcon status response
|
/// stores regex info for parsing the map line from rcon status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex MapStatus { get; }
|
ParserRegex MapStatus { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores regex info for parsing the gametype line from rcon status response
|
/// stores regex info for parsing the gametype line from rcon status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex GametypeStatus { get; }
|
ParserRegex GametypeStatus { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores regex info for parsing hostname line from rcon status response
|
/// stores regex info for parsing hostname line from rcon status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex HostnameStatus { get; }
|
ParserRegex HostnameStatus { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores regex info for parsing max players line from rcon status response
|
/// stores regex info for parsing max players line from rcon status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex MaxPlayersStatus { get; }
|
ParserRegex MaxPlayersStatus { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores the regex info for parsing get DVAR responses
|
/// stores the regex info for parsing get DVAR responses
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex Dvar { get; }
|
ParserRegex Dvar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// stores the regex info for parsing the header of a status response
|
/// stores the regex info for parsing the header of a status response
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ParserRegex StatusHeader { get; }
|
ParserRegex StatusHeader { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the expected response message from rcon when the server is not running
|
/// Specifies the expected response message from rcon when the server is not running
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string ServerNotRunningResponse { get; }
|
string ServerNotRunningResponse { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates if the application should wait for response from server
|
/// indicates if the application should wait for response from server
|
||||||
/// when executing a command
|
/// when executing a command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool WaitForResponse { get; }
|
bool WaitForResponse { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates the format expected for parsed guids
|
/// indicates the format expected for parsed guids
|
||||||
/// </summary>
|
/// </summary>
|
||||||
NumberStyles GuidNumberStyle { get; }
|
NumberStyles GuidNumberStyle { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies simple mappings for dvar names in scenarios where the needed
|
/// specifies simple mappings for dvar names in scenarios where the needed
|
||||||
/// information is not stored in a traditional dvar name
|
/// information is not stored in a traditional dvar name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, string> OverrideDvarNameMapping { get; }
|
IDictionary<string, string> OverrideDvarNameMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies the default dvar values for games that don't support certain dvars
|
/// specifies the default dvar values for games that don't support certain dvars
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, string> DefaultDvarValues { get; }
|
IDictionary<string, string> DefaultDvarValues { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// contains a setup of commands that have override timeouts
|
/// contains a setup of commands that have override timeouts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, int?> OverrideCommandTimeouts { get; }
|
IDictionary<string, int?> OverrideCommandTimeouts { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies how many lines can be used for ingame notice
|
/// specifies how many lines can be used for ingame notice
|
||||||
@ -87,29 +87,30 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies how many characters can be displayed per notice line
|
/// specifies how many characters can be displayed per notice line
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int NoticeMaxCharactersPerLine { get; }
|
int NoticeMaxCharactersPerLine { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// specifies the characters used to split a line
|
/// specifies the characters used to split a line
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string NoticeLineSeparator { get; }
|
string NoticeLineSeparator { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default port the game listens to RCon requests on
|
/// Default port the game listens to RCon requests on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int? DefaultRConPort { get; }
|
int? DefaultRConPort { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default Indicator of where the game is installed (ex file path or registry entry)
|
/// Default Indicator of where the game is installed (ex file path or registry entry)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string DefaultInstallationDirectoryHint { get; }
|
string DefaultInstallationDirectoryHint { get; set; }
|
||||||
|
|
||||||
ColorCodeMapping ColorCodeMapping { get; }
|
ColorCodeMapping ColorCodeMapping { get; set; }
|
||||||
|
|
||||||
|
short FloodProtectInterval { get; set; }
|
||||||
|
|
||||||
short FloodProtectInterval { get; }
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// indicates if diacritics (accented characters) should be normalized
|
/// indicates if diacritics (accented characters) should be normalized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ShouldRemoveDiacritics { get; }
|
bool ShouldRemoveDiacritics { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,10 @@ namespace SharedLibraryCore.Interfaces
|
|||||||
/// active clients
|
/// active clients
|
||||||
/// </summary>
|
/// </summary>
|
||||||
EFClient[] Clients { get; }
|
EFClient[] Clients { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// raw text data from the game server
|
||||||
|
/// </summary>
|
||||||
|
string[] RawResponse { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using SharedLibraryCore.Interfaces;
|
using SharedLibraryCore.Interfaces;
|
||||||
|
|
||||||
namespace SharedLibraryCore.Localization
|
namespace SharedLibraryCore.Localization
|
||||||
@ -8,6 +9,8 @@ namespace SharedLibraryCore.Localization
|
|||||||
{
|
{
|
||||||
private string localizationName;
|
private string localizationName;
|
||||||
|
|
||||||
|
public Layout() { }
|
||||||
|
|
||||||
public Layout(Dictionary<string, string> set)
|
public Layout(Dictionary<string, string> set)
|
||||||
{
|
{
|
||||||
LocalizationIndex = new TranslationLookup
|
LocalizationIndex = new TranslationLookup
|
||||||
@ -27,7 +30,7 @@ namespace SharedLibraryCore.Localization
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TranslationLookup LocalizationIndex { get; set; }
|
public TranslationLookup LocalizationIndex { get; set; }
|
||||||
public CultureInfo Culture { get; private set; }
|
[JsonIgnore] public CultureInfo Culture { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TranslationLookup : ITranslationLookup
|
public class TranslationLookup : ITranslationLookup
|
||||||
|
@ -120,6 +120,11 @@ namespace SharedLibraryCore.Database.Models
|
|||||||
[NotMapped]
|
[NotMapped]
|
||||||
public string TimeSinceLastConnectionString => (DateTime.UtcNow - LastConnection).HumanizeForCurrentCulture();
|
public string TimeSinceLastConnectionString => (DateTime.UtcNow - LastConnection).HumanizeForCurrentCulture();
|
||||||
|
|
||||||
|
public DateTimeOffset LastCommandExecutionAttempt { get; set; } = DateTimeOffset.MinValue;
|
||||||
|
|
||||||
|
[NotMapped]
|
||||||
|
public int CommandExecutionAttempts { get; set; }
|
||||||
|
|
||||||
[NotMapped]
|
[NotMapped]
|
||||||
// this is kinda dirty, but I need localizable level names
|
// this is kinda dirty, but I need localizable level names
|
||||||
public ClientPermission ClientPermission => new ClientPermission
|
public ClientPermission ClientPermission => new ClientPermission
|
||||||
|
@ -434,6 +434,7 @@ namespace SharedLibraryCore
|
|||||||
|
|
||||||
public abstract Task<long> GetIdForServer(Server server = null);
|
public abstract Task<long> GetIdForServer(Server server = null);
|
||||||
|
|
||||||
|
[Obsolete("Use the ScriptPluginExtension helper")]
|
||||||
public EFClient GetClientByNumber(int clientNumber) =>
|
public EFClient GetClientByNumber(int clientNumber) =>
|
||||||
GetClientsAsList().FirstOrDefault(client => client.ClientNumber == clientNumber);
|
GetClientsAsList().FirstOrDefault(client => client.ClientNumber == clientNumber);
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Library</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
|
||||||
<Version>2023.4.5.1</Version>
|
<Version>2024.01.01.1</Version>
|
||||||
<Authors>RaidMax</Authors>
|
<Authors>RaidMax</Authors>
|
||||||
<Company>Forever None</Company>
|
<Company>Forever None</Company>
|
||||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||||
@ -19,7 +19,7 @@
|
|||||||
<IsPackable>true</IsPackable>
|
<IsPackable>true</IsPackable>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<Description>Shared Library for IW4MAdmin</Description>
|
<Description>Shared Library for IW4MAdmin</Description>
|
||||||
<PackageVersion>2023.4.5.1</PackageVersion>
|
<PackageVersion>2024.06.22.1</PackageVersion>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<NoWarn>$(NoWarn);1591</NoWarn>
|
<NoWarn>$(NoWarn);1591</NoWarn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -34,23 +34,22 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="11.2.1" />
|
<PackageReference Include="FluentValidation" Version="11.9.2" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
||||||
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core.ru" Version="2.14.1" />
|
||||||
<PackageReference Include="Humanizer.Core.de" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core.de" Version="2.14.1" />
|
||||||
<PackageReference Include="Humanizer.Core.es" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core.es" Version="2.14.1" />
|
||||||
<PackageReference Include="Humanizer.Core.pt" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core.pt" Version="2.14.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="2.2.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Localization" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="8.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Serilog.AspNetCore" Version="8.0.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="6.0.1" />
|
|
||||||
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
<PackageReference Include="SimpleCrypto.NetCore" Version="1.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -60,11 +59,6 @@
|
|||||||
<IncludeAssets>Data.dll</IncludeAssets>
|
<IncludeAssets>Data.dll</IncludeAssets>
|
||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
|
||||||
<Exec Command="if not exist "$(ProjectDir)..\BUILD" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD"
)
)
if not exist "$(ProjectDir)..\BUILD\Plugins" (
if $(ConfigurationName) == Debug (
md "$(ProjectDir)..\BUILD\Plugins"
)
)" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -868,7 +868,14 @@ namespace SharedLibraryCore
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return await server.RconParser.GetStatusAsync(server.RemoteConnection, token);
|
var response = await server.RconParser.GetStatusAsync(server.RemoteConnection, token);
|
||||||
|
|
||||||
|
server.Manager.QueueEvent(new ServerStatusReceiveEvent
|
||||||
|
{
|
||||||
|
Response = response
|
||||||
|
});
|
||||||
|
|
||||||
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
catch (TaskCanceledException)
|
catch (TaskCanceledException)
|
||||||
@ -1333,6 +1340,14 @@ namespace SharedLibraryCore
|
|||||||
return serviceCollection;
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static TimeSpan GetExponentialBackoffDelay(int retryCount, int staticDelay = 5)
|
||||||
|
{
|
||||||
|
var maxTimeout = TimeSpan.FromMinutes(2.1);
|
||||||
|
const double factor = 2.0;
|
||||||
|
var delay = Math.Min(staticDelay + Math.Pow(factor, retryCount - 1), maxTimeout.TotalSeconds);
|
||||||
|
return TimeSpan.FromSeconds(delay);
|
||||||
|
}
|
||||||
|
|
||||||
public static void ExecuteAfterDelay(TimeSpan duration, Func<CancellationToken, Task> action, CancellationToken token = default) =>
|
public static void ExecuteAfterDelay(TimeSpan duration, Func<CancellationToken, Task> action, CancellationToken token = default) =>
|
||||||
ExecuteAfterDelay((int)duration.TotalMilliseconds, action, token);
|
ExecuteAfterDelay((int)duration.TotalMilliseconds, action, token);
|
||||||
|
|
||||||
|
@ -10,10 +10,9 @@ using SharedLibraryCore.Interfaces;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using WebfrontCore.ViewModels;
|
using WebfrontCore.ViewModels;
|
||||||
|
|
||||||
namespace WebfrontCore.Controllers
|
namespace WebfrontCore.Controllers
|
||||||
@ -92,15 +91,15 @@ namespace WebfrontCore.Controllers
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var file = JObject.Parse(content);
|
var jsonDocument = JsonDocument.Parse(content);
|
||||||
}
|
}
|
||||||
catch (JsonReaderException ex)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
return BadRequest($"{fileName}: {ex.Message}");
|
return BadRequest($"{fileName}: {ex.Message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = System.IO.Path.Join(Utilities.OperatingDirectory, "Configuration",
|
var path = Path.Join(Utilities.OperatingDirectory, "Configuration",
|
||||||
fileName.Replace($"{System.IO.Path.DirectorySeparatorChar}", ""));
|
fileName.Replace($"{Path.DirectorySeparatorChar}", ""));
|
||||||
|
|
||||||
// todo: move into a service at some point
|
// todo: move into a service at some point
|
||||||
if (!System.IO.File.Exists(path))
|
if (!System.IO.File.Exists(path))
|
||||||
|
@ -26,6 +26,11 @@ public class InteractionController : BaseController
|
|||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Client.Level < interactionData.MinimumPermission)
|
||||||
|
{
|
||||||
|
return Unauthorized();
|
||||||
|
}
|
||||||
|
|
||||||
ViewBag.Title = interactionData.Description;
|
ViewBag.Title = interactionData.Description;
|
||||||
var meta = HttpContext.Request.Query.ToDictionary(key => key.Key, value => value.Value.ToString());
|
var meta = HttpContext.Request.Query.ToDictionary(key => key.Key, value => value.Value.ToString());
|
||||||
var result = await _interactionRegistration.ProcessInteraction(interactionName, Client.ClientId, meta: meta, token: token);
|
var result = await _interactionRegistration.ProcessInteraction(interactionName, Client.ClientId, meta: meta, token: token);
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:61369/",
|
|
||||||
"sslPort": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,7 +15,7 @@
|
|||||||
<environment include="Development">
|
<environment include="Development">
|
||||||
<link rel="stylesheet" href="~/lib/halfmoon/css/halfmoon-variables.css"/>
|
<link rel="stylesheet" href="~/lib/halfmoon/css/halfmoon-variables.css"/>
|
||||||
<link rel="stylesheet" href="/css/src/main.css"/>
|
<link rel="stylesheet" href="/css/src/main.css"/>
|
||||||
<link rel="stylesheet" href="/css/open-iconic.css"/>
|
<link rel="stylesheet" href="/css/open-iconic-bootstrap-override.css"/>
|
||||||
</environment>
|
</environment>
|
||||||
<environment include="Production">
|
<environment include="Production">
|
||||||
<link rel="stylesheet" href="~/css/global.min.css?version=@ViewBag.Version"/>
|
<link rel="stylesheet" href="~/css/global.min.css?version=@ViewBag.Version"/>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RazorCompileOnBuild Condition="'$(CONFIG)'!='Debug'">true</RazorCompileOnBuild>
|
<RazorCompileOnBuild Condition="'$(CONFIG)'!='Debug'">true</RazorCompileOnBuild>
|
||||||
<RazorCompiledOnPublish Condition="'$(CONFIG)'!='Debug'">true</RazorCompiledOnPublish>
|
<RazorCompiledOnPublish Condition="'$(CONFIG)'!='Debug'">true</RazorCompiledOnPublish>
|
||||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||||
@ -33,22 +33,18 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Update="wwwroot\**\*.*" CopyToPublishDirectory="Never" />
|
<Content Update="wwwroot\**\*.*" CopyToPublishDirectory="Never" />
|
||||||
<Content Update="wwwroot\css\src\global.css" CopyToPublishDirectory="Never" />
|
|
||||||
<Content Update="wwwroot\css\src\global.min.css" CopyToPublishDirectory="Never" />
|
<Content Update="wwwroot\css\src\global.min.css" CopyToPublishDirectory="Never" />
|
||||||
<Content Update="Views\*.*" CopyToPublishDirectory="Never" />
|
<Content Update="Views\*.*" CopyToPublishDirectory="Never" />
|
||||||
<None Include="wwwroot\css\global.min.css" CopyToPublishDirectory="PreserveNewest" />
|
<None Include="wwwroot\css\global.min.css" CopyToPublishDirectory="PreserveNewest" />
|
||||||
<None Include="wwwroot\js\global.min.js" CopyToPublishDirectory="PreserveNewest" />
|
<None Include="wwwroot\js\global.min.js" CopyToPublishDirectory="PreserveNewest" />
|
||||||
<None Include="wwwroot\images\**\*.*" CopyToPublishDirectory="PreserveNewest" />
|
<None Include="wwwroot\images\**\*.*" CopyToPublishDirectory="PreserveNewest" />
|
||||||
<Content Remove="wwwroot\css\src\main.css.map" />
|
<None Remove="Properties\launchSettings.json" />
|
||||||
<Content Remove="dotnet-bundle.runtimeconfig.json" />
|
|
||||||
<Content Remove="dotnet-bundle.deps.json" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="BuildWebCompiler2022" Version="1.14.10" />
|
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.2.2" />
|
<PackageReference Include="Microsoft.AspNetCore.ConcurrencyLimiter" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.ConcurrencyLimiter" Version="6.0.16" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="8.0.6" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="6.0.8" />
|
|
||||||
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
<PackageReference Include="Microsoft.Web.LibraryManager.Build" Version="2.1.175" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@ -78,7 +74,4 @@
|
|||||||
</VisualStudio>
|
</VisualStudio>
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
|
||||||
<Exec Command="if $(ConfigurationName) == Debug ( 
powershell -Command wget https://raw.githubusercontent.com/iconic/open-iconic/master/font/css/open-iconic-bootstrap.scss -o $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss
echo d | xcopy /f /y $(ProjectDir)wwwroot\lib\open-iconic\font\fonts $(ProjectDir)wwwroot\font\
powershell -Command "((Get-Content -path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss -Raw) -replace '../fonts/','/font/') | Set-Content -Path $(ProjectDir)wwwroot\lib\open-iconic\font\css\open-iconic-bootstrap-override.scss"
)" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
"outputFileName": "wwwroot/css/global.min.css",
|
"outputFileName": "wwwroot/css/global.min.css",
|
||||||
"inputFiles": [
|
"inputFiles": [
|
||||||
"wwwroot/lib/halfmoon/css/halfmoon-variables.min.css",
|
"wwwroot/lib/halfmoon/css/halfmoon-variables.min.css",
|
||||||
"wwwroot/css/global.css",
|
|
||||||
"wwwroot/lib/chart.js/dist/Chart.min.css",
|
"wwwroot/lib/chart.js/dist/Chart.min.css",
|
||||||
"wwwroot/css/open-iconic.css"
|
"wwwroot/css/open-iconic-bootstrap-override.css",
|
||||||
|
"wwwroot/css/main.css"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"outputFile": "wwwroot/css/global.css",
|
|
||||||
"inputFile": "wwwroot/css/src/main.scss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"outputFile": "wwwroot/css/src/profile.css",
|
|
||||||
"inputFile": "wwwroot/css/src/profile.scss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"outputFile": "wwwroot/css/open-iconic.css",
|
|
||||||
"inputFile": "wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap-override.scss"
|
|
||||||
}
|
|
||||||
]
|
|
@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"compilers": {
|
|
||||||
"less": {
|
|
||||||
"autoPrefix": "",
|
|
||||||
"cssComb": "none",
|
|
||||||
"ieCompat": true,
|
|
||||||
"strictMath": false,
|
|
||||||
"strictUnits": false,
|
|
||||||
"relativeUrls": true,
|
|
||||||
"rootPath": "",
|
|
||||||
"sourceMapRoot": "",
|
|
||||||
"sourceMapBasePath": "",
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"sass": {
|
|
||||||
"includePath": "",
|
|
||||||
"indentType": "space",
|
|
||||||
"indentWidth": 2,
|
|
||||||
"outputStyle": "nested",
|
|
||||||
"Precision": 5,
|
|
||||||
"relativeUrls": true,
|
|
||||||
"sourceMapRoot": "",
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"stylus": {
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"babel": {
|
|
||||||
"sourceMap": false
|
|
||||||
},
|
|
||||||
"coffeescript": {
|
|
||||||
"bare": false,
|
|
||||||
"runtimeMode": "node",
|
|
||||||
"sourceMap": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"minifiers": {
|
|
||||||
"css": {
|
|
||||||
"enabled": true,
|
|
||||||
"termSemicolons": true,
|
|
||||||
"gzip": false
|
|
||||||
},
|
|
||||||
"javascript": {
|
|
||||||
"enabled": true,
|
|
||||||
"termSemicolons": true,
|
|
||||||
"gzip": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Reference in New Issue
Block a user