From f94c0e052ca87c3197506912713263bdfa795d4d Mon Sep 17 00:00:00 2001 From: linmeideli <2558527373@qq.com> Date: Sat, 23 May 2026 20:15:22 +0800 Subject: [PATCH] Translate Interface --- .dockerignore | 46 +- Dockerfile | 107 ++ Dockerfile.publish | 28 +- compose.yml | 13 +- ...mpostor.Api.Innersloth.Generator.deps.json | 369 ---- marketplace/Empostor.Api.deps.json | 316 ---- marketplace/Empostor.Api.pdb | Bin 98740 -> 0 bytes marketplace/Empostor.Api.xml | 1514 ----------------- .../Empostor.Plugin.Narrator.deps.json | 332 ---- marketplace/Empostor.Plugin.Narrator.pdb | Bin 18608 -> 0 bytes marketplace/Next.Hazel.Abstractions.deps.json | 23 - marketplace/Next.Hazel.Abstractions.pdb | Bin 11924 -> 0 bytes marketplace/Next.Hazel.deps.json | 117 -- marketplace/Next.Hazel.pdb | Bin 58020 -> 0 bytes src/Empostor.Api/Languages/LanguageService.cs | 68 +- .../Plugins/IPluginLanguageProvider.cs | 15 + .../NarratorPlugin.cs | 55 +- .../FriendCodeValidatorPlugin.cs | 22 +- .../Empostor.Plugins.MapVote.csproj | 10 + .../MapVoteCommand.cs | 135 ++ src/Empostor.Plugins.MapVote/MapVoteConfig.cs | 15 + .../MapVoteEventListener.cs | 62 + src/Empostor.Plugins.MapVote/MapVotePlugin.cs | 83 + .../MapVoteService.cs | 118 ++ .../MapVoteStartup.cs | 26 + .../packages.lock.json | 137 ++ .../Empostor.Plugins.Message.csproj | 10 + .../MessageCommand.cs | 109 ++ src/Empostor.Plugins.Message/MessageConfig.cs | 12 + .../MessageEventListener.cs | 71 + src/Empostor.Plugins.Message/MessagePlugin.cs | 68 + .../MessageStartup.cs | 26 + src/Empostor.Plugins.Message/MessageStore.cs | 121 ++ .../PendingMessage.cs | 25 + .../PlayerChannelCommand.cs | 16 +- .../PlayerChannelPlugin.cs | 28 +- .../QqVerifyCommand.cs | 23 +- .../QqVerifyPlugin.cs | 33 +- src/Empostor.Plugins.Titles/TitlesPlugin.cs | 22 +- src/Empostor.Server/Http/MonitorController.cs | 143 ++ src/Empostor.Server/Http/PrivacyController.cs | 135 ++ .../Plugins/PluginLoaderService.cs | 12 + src/Empostor.sln | 15 + 43 files changed, 1742 insertions(+), 2738 deletions(-) create mode 100644 Dockerfile delete mode 100644 marketplace/Empostor.Api.Innersloth.Generator.deps.json delete mode 100644 marketplace/Empostor.Api.deps.json delete mode 100644 marketplace/Empostor.Api.pdb delete mode 100644 marketplace/Empostor.Api.xml delete mode 100644 marketplace/Empostor.Plugin.Narrator.deps.json delete mode 100644 marketplace/Empostor.Plugin.Narrator.pdb delete mode 100644 marketplace/Next.Hazel.Abstractions.deps.json delete mode 100644 marketplace/Next.Hazel.Abstractions.pdb delete mode 100644 marketplace/Next.Hazel.deps.json delete mode 100644 marketplace/Next.Hazel.pdb create mode 100644 src/Empostor.Api/Plugins/IPluginLanguageProvider.cs create mode 100644 src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj create mode 100644 src/Empostor.Plugins.MapVote/MapVoteCommand.cs create mode 100644 src/Empostor.Plugins.MapVote/MapVoteConfig.cs create mode 100644 src/Empostor.Plugins.MapVote/MapVoteEventListener.cs create mode 100644 src/Empostor.Plugins.MapVote/MapVotePlugin.cs create mode 100644 src/Empostor.Plugins.MapVote/MapVoteService.cs create mode 100644 src/Empostor.Plugins.MapVote/MapVoteStartup.cs create mode 100644 src/Empostor.Plugins.MapVote/packages.lock.json create mode 100644 src/Empostor.Plugins.Message/Empostor.Plugins.Message.csproj create mode 100644 src/Empostor.Plugins.Message/MessageCommand.cs create mode 100644 src/Empostor.Plugins.Message/MessageConfig.cs create mode 100644 src/Empostor.Plugins.Message/MessageEventListener.cs create mode 100644 src/Empostor.Plugins.Message/MessagePlugin.cs create mode 100644 src/Empostor.Plugins.Message/MessageStartup.cs create mode 100644 src/Empostor.Plugins.Message/MessageStore.cs create mode 100644 src/Empostor.Plugins.Message/PendingMessage.cs create mode 100644 src/Empostor.Server/Http/MonitorController.cs create mode 100644 src/Empostor.Server/Http/PrivacyController.cs diff --git a/.dockerignore b/.dockerignore index 9163c62..2138431 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,9 +1,39 @@ -.dockerignore -.env +# Build output +**/bin/ +**/obj/ +**/publish/ + +# IDE +.idea/ +.vs/ +.vscode/ +*.user +*.suo + +# OS files +.DS_Store +Thumbs.db + +# Git +.git/ .gitignore -.vs -.vscode -**/.git -**/.idea -**/bin -**/obj \ No newline at end of file +.gitattributes + +# Docs +docs/ +*.md + +# Docker +Dockerfile* +.dockerignore +compose.yml +docker-compose*.yml + +# Logs +Log/ +*.log + +# Temp +tmp/ +temp/ +*.tmp diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1821bea --- /dev/null +++ b/Dockerfile @@ -0,0 +1,107 @@ +# ============================================ +# Stage 1: Build server + plugins from source +# ============================================ +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0 AS build + +ARG TARGETARCH +WORKDIR /src + +# Copy solution and project files first (layer caching) +COPY src/Empostor.sln ./ +COPY src/Directory.Build.props ./ 2>/dev/null || true +COPY src/ProjectRules.ruleset ./ 2>/dev/null || true + +# Server + API + Hazel +COPY src/Empostor.Server/Empostor.Server.csproj ./Empostor.Server/ +COPY src/Empostor.Api/Empostor.Api.csproj ./Empostor.Api/ +COPY src/Empostor.Api.Innersloth.Generator/Empostor.Api.Innersloth.Generator.csproj ./Empostor.Api.Innersloth.Generator/ +COPY src/NextFast.Hazel-New/Next.Hazel/Next.Hazel.csproj ./NextFast.Hazel-New/Next.Hazel/ +COPY src/NextFast.Hazel-New/Next.Hazel.Abstractions/Next.Hazel.Abstractions.csproj ./NextFast.Hazel-New/Next.Hazel.Abstractions/ + +# Plugins +COPY src/Empostor.Plugin.Chat/Empostor.Plugin.Chat.csproj ./Empostor.Plugin.Chat/ +COPY src/Empostor.Plugin.Code/Empostor.Plugin.Code.csproj ./Empostor.Plugin.Code/ +COPY src/Empostor.Plugin.Narrator/Empostor.Plugin.Narrator.csproj ./Empostor.Plugin.Narrator/ +COPY src/Empostor.Plugins.Example/Empostor.Plugins.Example.csproj ./Empostor.Plugins.Example/ +COPY src/Empostor.Plugins.FixedCode/Empostor.Plugins.FixedCode.csproj ./Empostor.Plugins.FixedCode/ +COPY src/Empostor.Plugins.FriendCodeValidator/Empostor.Plugins.FriendCodeValidator.csproj ./Empostor.Plugins.FriendCodeValidator/ +COPY src/Empostor.Plugins.PlayerChannel/Empostor.Plugins.PlayerChannel.csproj ./Empostor.Plugins.PlayerChannel/ +COPY src/Empostor.Plugins.QqVerify/Empostor.Plugins.QqVerify.csproj ./Empostor.Plugins.QqVerify/ +COPY src/Empostor.Plugins.Titles/Empostor.Plugins.Titles.csproj ./Empostor.Plugins.Titles/ +COPY src/Empostor.Plugins.Welcome/Empostor.Plugins.Welcome.csproj ./Empostor.Plugins.Welcome/ +COPY src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj ./Empostor.Plugins.MapVote/ + +# Restore dependencies +RUN dotnet restore Empostor.Server/Empostor.Server.csproj -a $TARGETARCH + +# Copy all source +COPY src/ ./ + +# Publish server (self-contained single file) +RUN dotnet publish Empostor.Server/Empostor.Server.csproj \ + -c Release \ + -a $TARGETARCH \ + --self-contained false \ + -o /publish/server + +# Publish plugins +RUN dotnet publish Empostor.Plugin.Chat/Empostor.Plugin.Chat.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugin.Code/Empostor.Plugin.Code.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugin.Narrator/Empostor.Plugin.Narrator.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.FixedCode/Empostor.Plugins.FixedCode.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.FriendCodeValidator/Empostor.Plugins.FriendCodeValidator.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.PlayerChannel/Empostor.Plugins.PlayerChannel.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.QqVerify/Empostor.Plugins.QqVerify.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.Titles/Empostor.Plugins.Titles.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.Welcome/Empostor.Plugins.Welcome.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore && \ + dotnet publish Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj \ + -c Release -a $TARGETARCH --self-contained false -o /publish/plugins --no-restore + +# ============================================ +# Stage 2: Runtime image +# ============================================ +FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime + +WORKDIR /app + +# Create directory structure +RUN mkdir -p /app/plugins /app/libraries /app/Languages /app/Log /app/config + +# Copy server +COPY --from=build /publish/server/ ./ + +# Copy plugins (only .dll files, skip dependency DLLs already in server) +COPY --from=build /publish/plugins/Empostor.Plugin.Chat.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugin.Code.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugin.Narrator.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.Example.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.FixedCode.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.FriendCodeValidator.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.PlayerChannel.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.QqVerify.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.Titles.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.Welcome.dll ./plugins/ +COPY --from=build /publish/plugins/Empostor.Plugins.MapVote.dll ./plugins/ + +# Copy marketplace data +COPY marketplace/ ./marketplace/ + +ENV ASPNETCORE_URLS=http://0.0.0.0:80 +ENV EMPOSTOR_Server__PublicIp=0.0.0.0 + +EXPOSE 80/tcp +EXPOSE 22023/tcp +EXPOSE 22023/udp + +VOLUME ["/app/config", "/app/plugins", "/app/Languages", "/app/Log", "/app/marketplace"] + +ENTRYPOINT ["./Empostor.Server"] diff --git a/Dockerfile.publish b/Dockerfile.publish index 0f9f61c..113234b 100644 --- a/Dockerfile.publish +++ b/Dockerfile.publish @@ -1,16 +1,34 @@ +# ============================================ +# Usage: +# 1. Publish the server: +# dotnet publish src/Empostor.Server -c Release -o publish +# 2. Build and run: +# docker compose -f compose.publish.yml up -d +# ============================================ FROM --platform=$TARGETPLATFORM mcr.microsoft.com/dotnet/aspnet:8.0 WORKDIR /app -# Copy pre-published output from host +# Copy pre-published server output COPY ./publish/ ./ -# Create directories for runtime data -RUN mkdir -p /app/Log /app/plugins /app/libraries +# Create runtime directories +RUN mkdir -p /app/plugins /app/libraries /app/Languages /app/Log -ENV ASPNETCORE_URLS= +# Copy plugins from build output (built separately) +# Place your plugin DLLs in ./plugins/ before building the image +COPY ./plugins/ ./plugins/ 2>/dev/null || true + +# Copy marketplace data +COPY ./marketplace/ ./marketplace/ 2>/dev/null || true + +ENV ASPNETCORE_URLS=http://0.0.0.0:80 ENV EMPOSTOR_Server__PublicIp=0.0.0.0 -EXPOSE 22023/tcp 22023/udp +EXPOSE 80/tcp +EXPOSE 22023/tcp +EXPOSE 22023/udp + +VOLUME ["/app/config", "/app/plugins", "/app/Languages", "/app/Log"] ENTRYPOINT ["./Empostor.Server"] diff --git a/compose.yml b/compose.yml index 0c52097..51e9170 100644 --- a/compose.yml +++ b/compose.yml @@ -2,7 +2,7 @@ services: empostor: build: context: . - dockerfile: Dockerfile.publish + dockerfile: Dockerfile container_name: empostor-server restart: unless-stopped ports: @@ -12,5 +12,14 @@ services: - ./config.json:/app/config.json:ro - ./Log:/app/Log - ./plugins:/app/plugins - - ./libraries:/app/libraries - ./Languages:/app/Languages + - ./marketplace:/app/marketplace + environment: + - ASPNETCORE_URLS=http://0.0.0.0:80 + - EMPOSTOR_Server__PublicIp=0.0.0.0 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80/api/admin/update/check"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 10s diff --git a/marketplace/Empostor.Api.Innersloth.Generator.deps.json b/marketplace/Empostor.Api.Innersloth.Generator.deps.json deleted file mode 100644 index 2d05061..0000000 --- a/marketplace/Empostor.Api.Innersloth.Generator.deps.json +++ /dev/null @@ -1,369 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETStandard,Version=v2.0/", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETStandard,Version=v2.0": {}, - ".NETStandard,Version=v2.0/": { - "Empostor.Api.Innersloth.Generator/1.10.6-dev": { - "dependencies": { - "CSharpPoet": "0.3.0", - "Microsoft.CodeAnalysis.CSharp": "4.7.0", - "System.Text.Encodings.Web": "8.0.0", - "System.Text.Json": "8.0.5" - }, - "runtime": { - "Empostor.Api.Innersloth.Generator.dll": {} - } - }, - "CSharpPoet/0.3.0": { - "runtime": { - "lib/netstandard2.0/CSharpPoet.dll": { - "assemblyVersion": "0.3.0.0", - "fileVersion": "0.3.0.0" - } - } - }, - "Microsoft.Bcl.AsyncInterfaces/8.0.0": { - "dependencies": { - "System.Threading.Tasks.Extensions": "4.5.4" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Bcl.AsyncInterfaces.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.CodeAnalysis.Common/4.7.0": { - "dependencies": { - "System.Collections.Immutable": "7.0.0", - "System.Memory": "4.5.5", - "System.Reflection.Metadata": "7.0.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encoding.CodePages": "7.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.CodeAnalysis.dll": { - "assemblyVersion": "4.7.0.0", - "fileVersion": "4.700.23.41609" - } - }, - "resources": { - "lib/netstandard2.0/cs/Microsoft.CodeAnalysis.resources.dll": { - "locale": "cs" - }, - "lib/netstandard2.0/de/Microsoft.CodeAnalysis.resources.dll": { - "locale": "de" - }, - "lib/netstandard2.0/es/Microsoft.CodeAnalysis.resources.dll": { - "locale": "es" - }, - "lib/netstandard2.0/fr/Microsoft.CodeAnalysis.resources.dll": { - "locale": "fr" - }, - "lib/netstandard2.0/it/Microsoft.CodeAnalysis.resources.dll": { - "locale": "it" - }, - "lib/netstandard2.0/ja/Microsoft.CodeAnalysis.resources.dll": { - "locale": "ja" - }, - "lib/netstandard2.0/ko/Microsoft.CodeAnalysis.resources.dll": { - "locale": "ko" - }, - "lib/netstandard2.0/pl/Microsoft.CodeAnalysis.resources.dll": { - "locale": "pl" - }, - "lib/netstandard2.0/pt-BR/Microsoft.CodeAnalysis.resources.dll": { - "locale": "pt-BR" - }, - "lib/netstandard2.0/ru/Microsoft.CodeAnalysis.resources.dll": { - "locale": "ru" - }, - "lib/netstandard2.0/tr/Microsoft.CodeAnalysis.resources.dll": { - "locale": "tr" - }, - "lib/netstandard2.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll": { - "locale": "zh-Hans" - }, - "lib/netstandard2.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll": { - "locale": "zh-Hant" - } - } - }, - "Microsoft.CodeAnalysis.CSharp/4.7.0": { - "dependencies": { - "Microsoft.CodeAnalysis.Common": "4.7.0" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.CodeAnalysis.CSharp.dll": { - "assemblyVersion": "4.7.0.0", - "fileVersion": "4.700.23.41609" - } - }, - "resources": { - "lib/netstandard2.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "cs" - }, - "lib/netstandard2.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "de" - }, - "lib/netstandard2.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "es" - }, - "lib/netstandard2.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "fr" - }, - "lib/netstandard2.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "it" - }, - "lib/netstandard2.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "ja" - }, - "lib/netstandard2.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "ko" - }, - "lib/netstandard2.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "pl" - }, - "lib/netstandard2.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "pt-BR" - }, - "lib/netstandard2.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "ru" - }, - "lib/netstandard2.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "tr" - }, - "lib/netstandard2.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "zh-Hans" - }, - "lib/netstandard2.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll": { - "locale": "zh-Hant" - } - } - }, - "System.Buffers/4.5.1": { - "runtime": { - "lib/netstandard2.0/System.Buffers.dll": { - "assemblyVersion": "4.0.3.0", - "fileVersion": "4.6.28619.1" - } - } - }, - "System.Collections.Immutable/7.0.0": { - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - }, - "runtime": { - "lib/netstandard2.0/System.Collections.Immutable.dll": { - "assemblyVersion": "7.0.0.0", - "fileVersion": "7.0.22.51805" - } - } - }, - "System.Memory/4.5.5": { - "dependencies": { - "System.Buffers": "4.5.1", - "System.Numerics.Vectors": "4.4.0", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - }, - "runtime": { - "lib/netstandard2.0/System.Memory.dll": { - "assemblyVersion": "4.0.1.2", - "fileVersion": "4.6.31308.1" - } - } - }, - "System.Numerics.Vectors/4.4.0": { - "runtime": { - "lib/netstandard2.0/System.Numerics.Vectors.dll": { - "assemblyVersion": "4.1.3.0", - "fileVersion": "4.6.25519.3" - } - } - }, - "System.Reflection.Metadata/7.0.0": { - "dependencies": { - "System.Collections.Immutable": "7.0.0", - "System.Memory": "4.5.5" - }, - "runtime": { - "lib/netstandard2.0/System.Reflection.Metadata.dll": { - "assemblyVersion": "7.0.0.0", - "fileVersion": "7.0.22.51805" - } - } - }, - "System.Runtime.CompilerServices.Unsafe/6.0.0": { - "runtime": { - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": { - "assemblyVersion": "6.0.0.0", - "fileVersion": "6.0.21.52210" - } - } - }, - "System.Text.Encoding.CodePages/7.0.0": { - "dependencies": { - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encoding.CodePages.dll": { - "assemblyVersion": "7.0.0.0", - "fileVersion": "7.0.22.51805" - } - } - }, - "System.Text.Encodings.Web/8.0.0": { - "dependencies": { - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "System.Text.Json/8.0.5": { - "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "8.0.0", - "System.Buffers": "4.5.1", - "System.Memory": "4.5.5", - "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "8.0.0", - "System.Threading.Tasks.Extensions": "4.5.4" - }, - "runtime": { - "lib/netstandard2.0/System.Text.Json.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.1024.46610" - } - } - }, - "System.Threading.Tasks.Extensions/4.5.4": { - "dependencies": { - "System.Runtime.CompilerServices.Unsafe": "6.0.0" - }, - "runtime": { - "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll": { - "assemblyVersion": "4.2.0.1", - "fileVersion": "4.6.28619.1" - } - } - } - } - }, - "libraries": { - "Empostor.Api.Innersloth.Generator/1.10.6-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "CSharpPoet/0.3.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3QDh9rMuiNhMtNWOLj0FItiZruON7lj3zD0CXC9IHq4JWpF5OjbaZ2s1X0u6gftE73KCoTezXIgKvxacps78Fw==", - "path": "csharppoet/0.3.0", - "hashPath": "csharppoet.0.3.0.nupkg.sha512" - }, - "Microsoft.Bcl.AsyncInterfaces/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==", - "path": "microsoft.bcl.asyncinterfaces/8.0.0", - "hashPath": "microsoft.bcl.asyncinterfaces.8.0.0.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.Common/4.7.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-pD5S14xMUebSGYe75kt0q/aaS/ftvktSo/pEv7aX7hNPHfdZS+SZeXvkvcffGxWkunYOyRF9m1oN7zzSdYj9dQ==", - "path": "microsoft.codeanalysis.common/4.7.0", - "hashPath": "microsoft.codeanalysis.common.4.7.0.nupkg.sha512" - }, - "Microsoft.CodeAnalysis.CSharp/4.7.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JHCP2L6lB0oJ3tQoHkC67SFZxW+KbJVOnAo+6L01K5r/NlBlSUhTk5nUAldWhTVwGdzqNeHqGtnEqpsCmGSwQA==", - "path": "microsoft.codeanalysis.csharp/4.7.0", - "hashPath": "microsoft.codeanalysis.csharp.4.7.0.nupkg.sha512" - }, - "System.Buffers/4.5.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==", - "path": "system.buffers/4.5.1", - "hashPath": "system.buffers.4.5.1.nupkg.sha512" - }, - "System.Collections.Immutable/7.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-dQPcs0U1IKnBdRDBkrCTi1FoajSTBzLcVTpjO4MBCMC7f4pDOIPzgBoX8JjG7X6uZRJ8EBxsi8+DR1JuwjnzOQ==", - "path": "system.collections.immutable/7.0.0", - "hashPath": "system.collections.immutable.7.0.0.nupkg.sha512" - }, - "System.Memory/4.5.5": { - "type": "package", - "serviceable": true, - "sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", - "path": "system.memory/4.5.5", - "hashPath": "system.memory.4.5.5.nupkg.sha512" - }, - "System.Numerics.Vectors/4.4.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ==", - "path": "system.numerics.vectors/4.4.0", - "hashPath": "system.numerics.vectors.4.4.0.nupkg.sha512" - }, - "System.Reflection.Metadata/7.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-MclTG61lsD9sYdpNz9xsKBzjsmsfCtcMZYXz/IUr2zlhaTaABonlr1ESeompTgM+Xk+IwtGYU7/voh3YWB/fWw==", - "path": "system.reflection.metadata/7.0.0", - "hashPath": "system.reflection.metadata.7.0.0.nupkg.sha512" - }, - "System.Runtime.CompilerServices.Unsafe/6.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", - "path": "system.runtime.compilerservices.unsafe/6.0.0", - "hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512" - }, - "System.Text.Encoding.CodePages/7.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-LSyCblMpvOe0N3E+8e0skHcrIhgV2huaNcjUUEa8hRtgEAm36aGkRoC8Jxlb6Ra6GSfF29ftduPNywin8XolzQ==", - "path": "system.text.encoding.codepages/7.0.0", - "hashPath": "system.text.encoding.codepages.7.0.0.nupkg.sha512" - }, - "System.Text.Encodings.Web/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==", - "path": "system.text.encodings.web/8.0.0", - "hashPath": "system.text.encodings.web.8.0.0.nupkg.sha512" - }, - "System.Text.Json/8.0.5": { - "type": "package", - "serviceable": true, - "sha512": "sha512-0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==", - "path": "system.text.json/8.0.5", - "hashPath": "system.text.json.8.0.5.nupkg.sha512" - }, - "System.Threading.Tasks.Extensions/4.5.4": { - "type": "package", - "serviceable": true, - "sha512": "sha512-zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", - "path": "system.threading.tasks.extensions/4.5.4", - "hashPath": "system.threading.tasks.extensions.4.5.4.nupkg.sha512" - } - } -} \ No newline at end of file diff --git a/marketplace/Empostor.Api.deps.json b/marketplace/Empostor.Api.deps.json deleted file mode 100644 index 0a789d1..0000000 --- a/marketplace/Empostor.Api.deps.json +++ /dev/null @@ -1,316 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v8.0", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETCoreApp,Version=v8.0": { - "Empostor.Api/1.10.6-dev": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "NextFast.Hazel": "1.0.2-dev", - "NextFast.Hazel.Abstractions": "1.0.2-dev", - "Next.Hazel.Abstractions": "1.0.2.0", - "Next.Hazel": "1.0.2.0" - }, - "runtime": { - "Empostor.Api.dll": {} - } - }, - "BouncyCastle.Cryptography/2.6.1": { - "runtime": { - "lib/net6.0/BouncyCastle.Cryptography.dll": { - "assemblyVersion": "2.0.0.0", - "fileVersion": "2.6.1.59591" - } - } - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { - "assemblyVersion": "2.2.0.0", - "fileVersion": "2.2.0.18316" - } - } - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { - "assemblyVersion": "2.2.0.0", - "fileVersion": "2.2.0.18316" - } - } - }, - "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.6": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { - "assemblyVersion": "9.0.0.0", - "fileVersion": "9.0.625.26613" - } - } - }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Hosting.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.ObjectPool/8.0.1": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.ObjectPool.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.123.58008" - } - } - }, - "Microsoft.Extensions.Options/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Options.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Primitives/8.0.0": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.Primitives.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Serilog/4.3.1-dev-02373": { - "runtime": { - "lib/net8.0/Serilog.dll": { - "assemblyVersion": "4.3.0.0", - "fileVersion": "4.3.0.0" - } - } - }, - "NextFast.Hazel/1.0.2-dev": { - "dependencies": { - "BouncyCastle.Cryptography": "2.6.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.ObjectPool": "8.0.1", - "NextFast.Hazel.Abstractions": "1.0.2-dev", - "Serilog": "4.3.1-dev-02373" - }, - "runtime": { - "Next.Hazel.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "NextFast.Hazel.Abstractions/1.0.2-dev": { - "runtime": { - "Next.Hazel.Abstractions.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "Next.Hazel.Abstractions/1.0.2.0": { - "runtime": { - "Next.Hazel.Abstractions.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "Next.Hazel/1.0.2.0": { - "runtime": { - "Next.Hazel.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - } - } - }, - "libraries": { - "Empostor.Api/1.10.6-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "BouncyCastle.Cryptography/2.6.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-vZsG2YILhthgRqO+ZVgRff4ZFKKTl0v7kqaVBLCtRvpREhfBP33pcWrdA3PRYgWuFL1RxiUFvjMUHTdBZlJcoA==", - "path": "bouncycastle.cryptography/2.6.1", - "hashPath": "bouncycastle.cryptography.2.6.1.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", - "path": "microsoft.aspnetcore.http.abstractions/2.2.0", - "hashPath": "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", - "path": "microsoft.aspnetcore.http.features/2.2.0", - "hashPath": "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", - "path": "microsoft.extensions.configuration.abstractions/8.0.0", - "hashPath": "microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.6": { - "type": "package", - "serviceable": true, - "sha512": "sha512-0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==", - "path": "microsoft.extensions.dependencyinjection.abstractions/9.0.6", - "hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.6.nupkg.sha512" - }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", - "path": "microsoft.extensions.diagnostics.abstractions/8.0.0", - "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", - "path": "microsoft.extensions.fileproviders.abstractions/8.0.0", - "hashPath": "microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", - "path": "microsoft.extensions.hosting.abstractions/8.0.0", - "hashPath": "microsoft.extensions.hosting.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "path": "microsoft.extensions.logging.abstractions/8.0.0", - "hashPath": "microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.ObjectPool/8.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA==", - "path": "microsoft.extensions.objectpool/8.0.1", - "hashPath": "microsoft.extensions.objectpool.8.0.1.nupkg.sha512" - }, - "Microsoft.Extensions.Options/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "path": "microsoft.extensions.options/8.0.0", - "hashPath": "microsoft.extensions.options.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Primitives/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "path": "microsoft.extensions.primitives/8.0.0", - "hashPath": "microsoft.extensions.primitives.8.0.0.nupkg.sha512" - }, - "Serilog/4.3.1-dev-02373": { - "type": "package", - "serviceable": true, - "sha512": "sha512-f0HNPaleOox5++8zqxAdi9afnUUkoznLOmd0ur/UnLbzl8bIaPyyBGOpbIJmC1kz2vHfkQ0fXh6KXMdlz7bcLQ==", - "path": "serilog/4.3.1-dev-02373", - "hashPath": "serilog.4.3.1-dev-02373.nupkg.sha512" - }, - "NextFast.Hazel/1.0.2-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "NextFast.Hazel.Abstractions/1.0.2-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "Next.Hazel.Abstractions/1.0.2.0": { - "type": "reference", - "serviceable": false, - "sha512": "" - }, - "Next.Hazel/1.0.2.0": { - "type": "reference", - "serviceable": false, - "sha512": "" - } - } -} \ No newline at end of file diff --git a/marketplace/Empostor.Api.pdb b/marketplace/Empostor.Api.pdb deleted file mode 100644 index 46bf7682bd2896b6c8f07cf46961f5e8cf41c419..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98740 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 diff --git a/marketplace/Empostor.Api.xml b/marketplace/Empostor.Api.xml deleted file mode 100644 index 5d72ff2..0000000 --- a/marketplace/Empostor.Api.xml +++ /dev/null @@ -1,1514 +0,0 @@ - - - - Empostor.Api - - - - A packet used a part of the network protocol that is unknown to Empostor, like a custom RPC. - - - A packet was sent at an inappropriate moment. - - - A packet was sent by a non-host player that should normally only be sent by the host. - - - A packet was sent that violated limits on the selection of player colors. - - - A packet was sent that exceeded the limits of possible nicknames to enter ingame. - - - A packet was sent on behalf of another player. - - - An ability was used that the current role cannot access. - - - A packet was sent to a player that should be broadcasted, or vice versa. - - - A packet was sent on an invalid network object, like a PlayerControl without PlayerInfo. - - - Legacy category for unsorted anticheat checks. - - - - Details if exceptions are made for hosts that are cheating. - - - - - Hosts follow the same policies as other players. - - - - - Hosts are allowed to cheat if they request HostAuthority. If they - do not request this, the same policies as for other players applies. - - - HostAuthority can be requested by hosts by adding 25 to their patch - version when connecting. This flag is used by a lot of (host-only) - mods and also disable server authority over MurderPlayer packets. - - - - - Hosts are always allowed to cheat. - - - - - Configuration for HttpServer. - - - - - Gets the name of this config section. - - - - - Gets or sets the IP address the HTTP Matchmaking server will listen on. - - Use "127.0.0.1" if you are running behind a reverse proxy or just testing locally. - Use "0.0.0.0" if you are directly exposing this server to the internet. - - - - Gets or sets the port the HTTP Matchmaking server will listen on. - - For port forwarding purposes, this is a TCP port. - - - - Gets or sets the priority of the event listener. - - - - - Gets or sets the event that the listener is listening to. - - - - - Gets or sets a value indicating whether the listener will be called regardless of the . - - - - - Called just after a is created and connected. - - - - - Called just before a is registered. - - - - - Called whenever a new is created. - - - Note that the game just has been created, so no players have joined - it yet. If you want to know the future host of this game, use the - property. - - - - - Gets the client that requested creation of the game. - - - Will be null if game creation was requested by a plugin. - - - - - Called just before a new is created. - - - - - Gets the client that requested creation of the game. - - - Will be null if game creation was requested by a plugin. - - - - - Gets or sets the desired . - - If the GameCode is invalid or already used in another game. - - - - Called whenever a new is destroyed. - - - - - Gets the this event belongs to. - - - - - This event is triggered when game options are going to be changed. - - - Be careful when calling SyncSettingsAsync while handling this event, - as it may call this event again in some cases. - - - - - Lists the possible reasons that the game options may have changed. - - - - - The options were changed by the host using LogicOptions deserialization. - - - This event does not change the message sent to other players, - so changes made to Game.Options are not synced until - SyncSettingsAsync is called afterwards. - - - - - The options were changed by a plugin using the - SyncSettingsAsync method in . - - - This event is called after serializing the settings, which - means that changes made by event handlers will not be synced - over. - - We strongly recommend against calling SyncSettingsAsync while - handling this event. - - - - - Gets the reason that the game options have changed. - - - - - Called just before a joins a game. - - - - - The game is started here and players have been initialized. - - - - - Called when the game is going to start. - When this is called, not all players are initialized properly yet. - If you want to get correct player states, use . - - - - - Gets the exiled player. - - - - - Gets a value indicating whether meeting result is a tie. - - - - - Gets the message sent by the player. - - - - - Gets or sets a value indicating whether the message is sent to - all players (true) or only to the host (false). - To not send this message at all, cancel this event instead. - - - - - Event that allows changing or canceling an upcoming player murder. - - - Note that this event only triggers if the Host of the Game did not disable server authority. - - If you want to get an event after the murder took place, listen to . - - - - - Gets the player who will be murdered. - - - - - Gets or sets the result of this event. - - - Its initial value is what would have happened in the normal game flow. - - If set to Succeeded, the Victim will die. - - If set to FailedProtected, the Victim will be protected with a shield animation and the attacker will have a cooldown set. - - - - - Called whenever a player enters a vent. - - - - - Gets the entered vent. - - - - - Gets the that triggered this . - - - - - Gets the networked that triggered this . - This belongs to the . - - - - - Called whenever a player gets exiled (voted out). - - - - - Called whenever a player exits a vent. - - - - - Gets the exited vent. - - - - - Event that is called when a player is killed by another player. - - - This event works regardless of server authority is enabled or not. - - If you want to cancel this kill, listen to . - If you want to know about players that were voted out, listed to . - - - - - Gets the player who got murdered. - - - - - Gets the result of the event. - - - Note that if FailedError or FailedProtected is set, the kill did not take place. - - - - - Gets the current time of the start counter. - - - - - Gets the player who's body got reported. Is null when the meeting started by Emergency call button. - - - - - Called whenever a player moves to another vent. - - - - - Gets the vent player moved to. - - - - - Gets the player he voted for. - - - - - Gets the Vote type. - - - - - Gets or sets a value indicating whether the event was cancelled. - - - - - Register a temporary event listener. - - Event listener. - Middleware between the events, which can be used to swap to the correct thread dispatcher. - Disposable that unregisters the callback from the event manager. - Type of the event listener. - - - - Returns true if an event with the type is registered. - - True if the is registered. - Type of the event. - - - - Call all the event listeners for the type . - - The event argument. - Type of the event. - A representing the asynchronous operation. - - - - Disposes multiple . - - - - Returns a read-only wrapper for the dictionary. - An to create a from. - The type of the keys of . - The type of the values of . - An object that acts as a read-only wrapper around the . - - - - Priovides a StreamReader-like api throught extensions. - - - - - Advances the position of by the size of . - - Type that will be read. - input "stream"/span. - The original input. - - - - - - - - - - - - - No error occured while joining the game. - - - - - The client is not registered in the client manager. - - - - - The client has been banned from the game. - - - - - The game is full. - - - - - The limbo state of the player is incorrect. - - - - - The game is already started. - - - - - The game has been destroyed. - - - - - The host has a newer version of the game and the client should update. - - - - - The host has an older version of the game and the client should downgrade. - - - - - Custom error by a plugin. - - - A custom message can be set in . - - - - - Gets or sets display name on game list. - - - - - Gets a value indicating whether the Host of the game has requested host authority. - - - Vanilla Among Us does not request this, but certain client-side mods will. - - - - - Adds an to the ban list of this game. - Prevents all future joins from this . - This does not kick the player with that from the lobby. - - - The to ban. - - - - - Syncs the internal to all players. - Necessary to do if you modified it, otherwise it won't be used. - - A representing the asynchronous operation. - - - - Sets game's privacy. - - Privacy to set. - A representing the asynchronous operation. - - - - Send the message to all players. - - Message to send. - Required limbo state of the player. - A representing the asynchronous operation. - - - - Send the message to all players except one. - - Message to send. - The player to exclude from sending the message. - Required limbo state of the player. - A representing the asynchronous operation. - - - - Send a message to a specific player. - - Message to send. - ID of the client. - A representing the asynchronous operation. - - - - Start a GameData(To) message. - - Use with caution, recommended only for advanced developers. - The client to target if needed, `null` if the message should be sent to all players. - Message type of the message. - A that you can fill and send using . - - - - Finishes GameData message and sends it to either the target or all players. - - Use with caution, recommended only for advanced developers. - MessageWriter received from . - Same target ClientId passed to StartGameData. - Task that sends the packet. - - - - Creates a MessageWriter with GameData header. - - Use with caution, recommended only for advanced developers. - Net id of the InnerNetObject. - Rpc id of the message. - The client to target if needed, `null` if the message should be sent to all players. - Message type of the message. - A that you can fill with rpc data and send using . - - - - Finishes rpc message and sends it to either the target or all players. - - Use with caution, recommended only for advanced developers. - Message writer of the rpc. - Same target client id passed to . - A representing the asynchronous operation. - - - - Creates a new game. - - Game options. - Filter options. - Created game or null if creation was cancelled by a plugin. - Thrown when game creation failed. - - - - Register a method to filter listings on. - - - - - Return a filter to filter listings on. - - HTTP Context of this request. - A function that looks at a game and returns true iff the connecting player is compatible with this game. - - - - Gets a value indicating whether a player outfit is complete or whether it needs to receive additional cosmetics. - - true if the outfit is incomplete, false if the outfit is complete. - - - - Tag that can be added to indicate the expected skill level of players in this lobby. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Gets or sets the Player speed modifier. - - - - - Gets or sets the Light modifier for the players that are members of the crew as a multiplier value. - - - - - Gets or sets the Light modifier for the players that are Empostors as a multiplier value. - - - - - Gets or sets the number of common tasks. - - - - - Gets or sets the number of long tasks. - - - - - Gets or sets the number of short tasks. - - - - - Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. - - - - - Gets the version. - - - - - Gets the currently active gamemode. This is currently used for the Normal and HideAndSeek gamemodes. - - - - - Gets the currently active special gamemode. This is currently used for the AprilFools gamemode. - - - - - Gets the rule preset. - - - - - Gets or sets the maximum amount of players for this lobby. - - - - - Gets or sets the language of the lobby as per enum. - - - - - Gets or sets the Map selected for this lobby. - - - - - Gets or sets the number of impostors for this lobby. - - - - - Gets or sets a value indicating whether the GameOptions are the default ones. - - - - - The latest major version of the game client. - - - - - Gets or sets host's version of the game. - - - - - - - - - - - Gets or sets the maximum amount of players for this lobby. - - - - - Gets or sets the language of the lobby as per enum. - - - - - Gets or sets the Map selected for this lobby. - - - - - Gets or sets the Player speed modifier. - - - - - Gets or sets the Light modifier for the players that are members of the crew as a multiplier value. - - - - - Gets or sets the Light modifier for the players that are Empostors as a multiplier value. - - - - - Gets or sets the Empostor cooldown to kill in seconds. - - - - - Gets or sets the number of common tasks. - - - - - Gets or sets the number of long tasks. - - - - - Gets or sets the number of short tasks. - - - - - Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds. - - - - - Gets or sets the cooldown between each time any player can call an emergency meeting in seconds. - - - - - Gets or sets the number of impostors for this lobby. - - - - - Gets or sets a value indicating whether ghosts (dead crew members) can do tasks. - - - - - Gets or sets the Kill as per values in . - - - - - Gets or sets the time for discussion before voting time in seconds. - - - - - Gets or sets the time for voting in seconds. - - - - - Gets or sets a value indicating whether an ejected player is an impostor or not. - - - - - Gets or sets a value indicating whether players are able to see tasks being performed by other players. - - - By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players. - - - - - Gets or sets a value indicating whether the vote is anonymous. - - - - - Gets or sets the task bar update mode as per values in . - - - - - Gets or sets role options. - - - - - Gets or sets a value indicating whether the GameOptions are the default ones. - - - - - Serializes this instance of GameOptionsData object to a specified BinaryWriter. - - The stream to write the message to. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Gets or sets the Player speed modifier. - - - - - Gets or sets the Light modifier for the players that are members of the crew as a multiplier value. - - - - - Gets or sets the Light modifier for the players that are Empostors as a multiplier value. - - - - - Gets or sets the Empostor cooldown to kill in seconds. - - - - - Gets or sets the number of common tasks. - - - - - Gets or sets the number of long tasks. - - - - - Gets or sets the number of short tasks. - - - - - Gets or sets the maximum amount of emergency meetings each player can call during the game in seconds. - - - - - Gets or sets the cooldown between each time any player can call an emergency meeting in seconds. - - - - - Gets or sets a value indicating whether ghosts (dead crew members) can do tasks. - - - - - Gets or sets the Kill as per values in . - - - - - Gets or sets the time for discussion before voting time in seconds. - - - - - Gets or sets the time for voting in seconds. - - - - - Gets or sets a value indicating whether an ejected player is an impostor or not. - - - - - Gets or sets a value indicating whether players are able to see tasks being performed by other players. - - - By being set to true, tasks such as Empty Garbage, Submit Scan, Clear asteroids, Prime shields execution will be visible to other players. - - - - - Gets or sets a value indicating whether the vote is anonymous. - - - - - Gets or sets the task bar update mode as per values in . - - - - - Gets or sets the experience level of people in the lobby: Beginner, Intermediate or Expert. - - - - - Gets a value indicating whether the DisableServerAuthority flag is present. - - - - - Normalizes this game version by removing all the special flags. - - This GameVersion but stripped of special flags. - - - - Register a custom message. - - message. - Disposable that unregisters the message. - - - - Represents a connected game client. - - - - - Gets or sets the unique ID of the client. - - - This ID is generated when the client is registered in the client manager and should not be used - to store persisted data. - - - - - Gets the name that was provided by the player in the client. - - - The name is provided by the player and should not be used to store persisted data. - - - - - Gets the language. - - - - - Gets the quick chat mode. - - - - - Gets the connection of the client. - - - Null when the client was not registered by the matchmaker. - - - - - Gets a key/value collection that can be used to share data between messages. - - - - The stored data will not be saved. - After the connection has been closed all data will be lost. - - - Note that the values will not be disposed after the connection has been closed. - This has to be implemented by the plugin. - - - - - - Gets the current game data of the . - - - - - Gets the version of the game the client is using. - - - - - Gets platform specific data of the . - - - - - Mainly feature of Empostor - the ONLY code for a player and easy to remenber. - - - - - Disconnect the client with a . - - - The message to show to the player. - - - Only used when is set to . - - - A representing the asynchronous operation. - - - - - Represents a player in . - - - - - Gets the client that belongs to the player. - - - - - Gets the game where the belongs to. - - - - - Gets or sets the current limbo state of the player. - - - - - Checks if the specified is owned by . - - The . - Returns true if owned by . - - - - Represents the connection of the client. - - - - - Gets the IP endpoint of the client. - - - - - Gets a value indicating whether the client is connected to the server. - - - - - Gets or sets the client of the connection. - - - - - Gets the average ping of the client. - - - - - Sends a message writer to the connection. - - The message. - Task that must be awaited. - - - - Disconnects the client and invokes the disconnect handler. - - A reason. - A message to send with disconnect packet. - Task that must be awaited. - - - - Holds all data that is serialized over the network through GameData packets. - - - - - Gets position where the object thinks it is (not interpolated). - - - - - Snaps the current to the given position . - - The target position. - Task that must be awaited. - - - - Gets the player info of this vote state. - - - - - Gets a value indicating whether the player is a dead. - - - - - Gets a value indicating whether the player started this meeting. - - - - - Gets a value indicating whether the player voted. - - - - - Gets the vote type. - - - Null when is false. - - - - - Gets the player voted for. - - - Null when isn't . - - - - - Gets states of players taking part in this meeting. - - - - - Gets the player that started the meeting. - - - - - Gets the assigned by the client of the host of the game. - - - - - Gets the of the . - Contains vent logic. - - - - - Gets the of the . - Contains position data about the player. - - - - - Gets the of the . - Contains metadata about the player. - - - - - Sets the name of the current . - Visible to all players. - - A name for the player. - Thrown when player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Sets the color of the current . - Visible to all players. - - A color for the player. - Thrown when player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Sets the hat of the current . - Visible to all players. - - An hat for the player. - Thrown when player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Sets the pet of the current . - Visible to all players. - - A pet for the player. - Thrown when player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Sets the skin of the current . - Visible to all players. - - A skin for the player. - Thrown when player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Sets the visor of the current . - Visible to all players. - - A visor for the player. - Task that must be awaited. - - - - Sets the nameplate of the current . - Visible to all players. - - A nameplate for the player. - Task that must be awaited. - - - - Send a chat message as the current . - Visible to all players. - - The message to send. - Task that must be awaited. - - - - Send a chat message as the current . - Visible to only the current. - - The message to send. - - The player that should receive this chat message. - When left as null, will send message to self. - - Task that must be awaited. - - - - Murder player or remove their protective shield. - - Target player to murder. - The result of the murder operation. - Thrown when player is not the impostor. - Thrown when player is dead. - Thrown when target is dead. - Thrown when player or target player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Murder player. - - Target player to murder. - The result of the murder operation. - Task that must be awaited. - - - - Murder player after validating if this is a valid kill. - - Target player to murder. - Thrown when player is not the impostor. - Thrown when player is dead. - Thrown when target is dead. - Thrown when player or target player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Protect player. - - Target player to protect. - Thrown when target is dead. - Thrown when player or target player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Protect player. - - Target player to protect. - Task that must be awaited. - - - - Exile the current player. This doesn't produce a body to be reported. - Visible to all players. - - Thrown if player to be exiled is already dead. - Thrown when player or target player doesn't have a PlayerInfo. - Task that must be awaited. - - - - Exile the current player. This doesn't produce a body to be reported. - Visible to all players. - - Task that must be awaited. - - - - Gets the name of the player as decided by the host. - - - - - Gets the role type of the player. - - - - - Gets a value indicating whether the player is an impostor. - - - - - Gets a value indicating whether the player is a dead in the current game. - - - - - Gets the reason why the player is dead in the current game. - - - - - Sets the task as complete. - - Task that must be awaited. - - - - Maintains an internal compatibility list of versions that are allowed to connect to the server, and which game - versions they are allowed to play with. - - - - - Gets the compatibility groups. - - - - - Check if a client can join the server according to the currently accepted game versions. - - The client version to check for. - - Whether this version is supported by the server at the moment and if not, whether it is too old or too new. - - - - Check if a player can join an existing game. - The client version of the host. - The client version of the player that is joining. - - - if everything is OK. - if the player runs a too old game version. - if the player runs a too new game version. - - - - - - Add a new compatibility group. - - WARNING: this method does not magically make changes to Empostor to properly support these versions. If - Empostor cannot support the game versions you're trying to add or that the game versions you're making - compatible do not crossplay correctly, weird behaviour may occur. Here be dragons. - - The compatibility group to add. - - - - Add a supported game version to the specified compatibility group. - - WARNING: this method does not magically make changes to Empostor to properly support these versions. If - Empostor cannot support the game versions you're trying to add or that the game versions you're making - compatible do not crossplay correctly, weird behaviour may occur. Here be dragons. - - The compatibility group to add this version to. - The game version to add. - - - - Remove a version from the internal version compatibility list. - - Note that this will not stop players currently connected to the server from playing, it will only stop new - connections. - The version to remove from the list. - True iff this version was on the current compatibility list. - - - - Deserializes a UDP handshake packet. - - Actual UDP (non-DTLS) handshake packet layout (based on Among Us client and preview project): - GameVersion (4 bytes) - Name (string) - [V1+] LastNonce / uint32 (4 bytes) — ignored by server - [V2+] Language (uint32) + ChatMode (byte) - [V3+] PlatformSpecificData (message) + ProductUserId (string, client-reported) + CrossplayFlags (uint32) - - Note: UDP handshake packets do not include matchmakerToken or friendCode. - Authentication data is exchanged beforehand via the HTTP /api/user endpoint - and associated on the server side by IP address. - - - - - Clamps the given value between the given minimum float and maximum float values. Returns the given value if it is within the min and max range. - - The floating point value to restrict inside the range defined by the min and max values. - The minimum floating point value to compare against. - The maximum floating point value to compare against. - - The float result between the min and max values. - - - - - Clamps value between 0 and 1 and returns value. - - Value. - Clamped value. - - - - Linearly interpolates between a and b by t. - - The start value. - The end value. - The interpolation value between the two floats. - - The interpolated float result between the two float values. - - - - diff --git a/marketplace/Empostor.Plugin.Narrator.deps.json b/marketplace/Empostor.Plugin.Narrator.deps.json deleted file mode 100644 index 21a0f95..0000000 --- a/marketplace/Empostor.Plugin.Narrator.deps.json +++ /dev/null @@ -1,332 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v8.0", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETCoreApp,Version=v8.0": { - "Empostor.Plugin.Narrator/1.10.6-dev": { - "dependencies": { - "Empostor.Api": "1.10.6-dev", - "Next.Hazel.Abstractions": "1.0.2.0", - "Next.Hazel": "1.0.2.0" - }, - "runtime": { - "Empostor.Plugin.Narrator.dll": {} - } - }, - "BouncyCastle.Cryptography/2.6.1": { - "runtime": { - "lib/net6.0/BouncyCastle.Cryptography.dll": { - "assemblyVersion": "2.0.0.0", - "fileVersion": "2.6.1.59591" - } - } - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": { - "assemblyVersion": "2.2.0.0", - "fileVersion": "2.2.0.18316" - } - } - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": { - "assemblyVersion": "2.2.0.0", - "fileVersion": "2.2.0.18316" - } - } - }, - "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Configuration.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.6": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": { - "assemblyVersion": "9.0.0.0", - "fileVersion": "9.0.625.26613" - } - } - }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Options": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Diagnostics.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.FileProviders.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", - "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Hosting.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.ObjectPool/8.0.1": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.ObjectPool.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.123.58008" - } - } - }, - "Microsoft.Extensions.Options/8.0.0": { - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.Primitives": "8.0.0" - }, - "runtime": { - "lib/net8.0/Microsoft.Extensions.Options.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Microsoft.Extensions.Primitives/8.0.0": { - "runtime": { - "lib/net8.0/Microsoft.Extensions.Primitives.dll": { - "assemblyVersion": "8.0.0.0", - "fileVersion": "8.0.23.53103" - } - } - }, - "Serilog/4.3.1-dev-02373": { - "runtime": { - "lib/net8.0/Serilog.dll": { - "assemblyVersion": "4.3.0.0", - "fileVersion": "4.3.0.0" - } - } - }, - "Empostor.Api/1.10.6-dev": { - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", - "Microsoft.Extensions.Logging.Abstractions": "8.0.0", - "NextFast.Hazel": "1.0.2-dev", - "NextFast.Hazel.Abstractions": "1.0.2-dev" - }, - "runtime": { - "Empostor.Api.dll": { - "assemblyVersion": "1.10.6.0", - "fileVersion": "1.10.6.0" - } - } - }, - "NextFast.Hazel/1.0.2-dev": { - "dependencies": { - "BouncyCastle.Cryptography": "2.6.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "9.0.6", - "Microsoft.Extensions.ObjectPool": "8.0.1", - "NextFast.Hazel.Abstractions": "1.0.2-dev", - "Serilog": "4.3.1-dev-02373" - }, - "runtime": { - "Next.Hazel.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "NextFast.Hazel.Abstractions/1.0.2-dev": { - "runtime": { - "Next.Hazel.Abstractions.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "Next.Hazel.Abstractions/1.0.2.0": { - "runtime": { - "Next.Hazel.Abstractions.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - }, - "Next.Hazel/1.0.2.0": { - "runtime": { - "Next.Hazel.dll": { - "assemblyVersion": "1.0.2.0", - "fileVersion": "1.0.2.0" - } - } - } - } - }, - "libraries": { - "Empostor.Plugin.Narrator/1.10.6-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "BouncyCastle.Cryptography/2.6.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-vZsG2YILhthgRqO+ZVgRff4ZFKKTl0v7kqaVBLCtRvpREhfBP33pcWrdA3PRYgWuFL1RxiUFvjMUHTdBZlJcoA==", - "path": "bouncycastle.cryptography/2.6.1", - "hashPath": "bouncycastle.cryptography.2.6.1.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", - "path": "microsoft.aspnetcore.http.abstractions/2.2.0", - "hashPath": "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512" - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", - "path": "microsoft.aspnetcore.http.features/2.2.0", - "hashPath": "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512" - }, - "Microsoft.Extensions.Configuration.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", - "path": "microsoft.extensions.configuration.abstractions/8.0.0", - "hashPath": "microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/9.0.6": { - "type": "package", - "serviceable": true, - "sha512": "sha512-0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==", - "path": "microsoft.extensions.dependencyinjection.abstractions/9.0.6", - "hashPath": "microsoft.extensions.dependencyinjection.abstractions.9.0.6.nupkg.sha512" - }, - "Microsoft.Extensions.Diagnostics.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", - "path": "microsoft.extensions.diagnostics.abstractions/8.0.0", - "hashPath": "microsoft.extensions.diagnostics.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.FileProviders.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", - "path": "microsoft.extensions.fileproviders.abstractions/8.0.0", - "hashPath": "microsoft.extensions.fileproviders.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Hosting.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", - "path": "microsoft.extensions.hosting.abstractions/8.0.0", - "hashPath": "microsoft.extensions.hosting.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Logging.Abstractions/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", - "path": "microsoft.extensions.logging.abstractions/8.0.0", - "hashPath": "microsoft.extensions.logging.abstractions.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.ObjectPool/8.0.1": { - "type": "package", - "serviceable": true, - "sha512": "sha512-X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA==", - "path": "microsoft.extensions.objectpool/8.0.1", - "hashPath": "microsoft.extensions.objectpool.8.0.1.nupkg.sha512" - }, - "Microsoft.Extensions.Options/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", - "path": "microsoft.extensions.options/8.0.0", - "hashPath": "microsoft.extensions.options.8.0.0.nupkg.sha512" - }, - "Microsoft.Extensions.Primitives/8.0.0": { - "type": "package", - "serviceable": true, - "sha512": "sha512-bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==", - "path": "microsoft.extensions.primitives/8.0.0", - "hashPath": "microsoft.extensions.primitives.8.0.0.nupkg.sha512" - }, - "Serilog/4.3.1-dev-02373": { - "type": "package", - "serviceable": true, - "sha512": "sha512-f0HNPaleOox5++8zqxAdi9afnUUkoznLOmd0ur/UnLbzl8bIaPyyBGOpbIJmC1kz2vHfkQ0fXh6KXMdlz7bcLQ==", - "path": "serilog/4.3.1-dev-02373", - "hashPath": "serilog.4.3.1-dev-02373.nupkg.sha512" - }, - "Empostor.Api/1.10.6-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "NextFast.Hazel/1.0.2-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "NextFast.Hazel.Abstractions/1.0.2-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - }, - "Next.Hazel.Abstractions/1.0.2.0": { - "type": "reference", - "serviceable": false, - "sha512": "" - }, - "Next.Hazel/1.0.2.0": { - "type": "reference", - "serviceable": false, - "sha512": "" - } - } -} \ No newline at end of file diff --git a/marketplace/Empostor.Plugin.Narrator.pdb b/marketplace/Empostor.Plugin.Narrator.pdb deleted file mode 100644 index 0cf64032876598e12a139cd708c77632b211dca2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18608 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 diff --git a/marketplace/Next.Hazel.Abstractions.deps.json b/marketplace/Next.Hazel.Abstractions.deps.json deleted file mode 100644 index e787a9e..0000000 --- a/marketplace/Next.Hazel.Abstractions.deps.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "runtimeTarget": { - "name": ".NETCoreApp,Version=v8.0", - "signature": "" - }, - "compilationOptions": {}, - "targets": { - ".NETCoreApp,Version=v8.0": { - "Next.Hazel.Abstractions/1.0.2-dev": { - "runtime": { - "Next.Hazel.Abstractions.dll": {} - } - } - } - }, - "libraries": { - "Next.Hazel.Abstractions/1.0.2-dev": { - "type": "project", - "serviceable": false, - "sha512": "" - } - } -} \ No newline at end of file diff --git a/marketplace/Next.Hazel.Abstractions.pdb b/marketplace/Next.Hazel.Abstractions.pdb deleted file mode 100644 index 993b4e3a7b0a18c95ed64a879f4d9479f4a9ef0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11924 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 + /// Register translations provided by a plugin. + /// Merges into existing language tables without overwriting existing keys. + /// + public void AddPluginTranslations(IReadOnlyDictionary> translations) + { + foreach (var (langCode, table) in translations) + { + if (!_tables.TryGetValue(langCode, out var existing)) + { + existing = new Dictionary(); + _tables[langCode] = existing; + } + + foreach (var (key, value) in table) + { + if (!existing.ContainsKey(key)) + { + existing[key] = value; + } + } + + _logger.LogDebug("[Language] Plugin added {Count} keys for {Lang}", table.Count, langCode); + } + } + private string? Lookup(string key, string langCode) { return _tables.TryGetValue(langCode, out var table) && table.TryGetValue(key, out var val) @@ -163,19 +189,7 @@ public sealed class LanguageService "command.players.not_in_lobby": "This command can only be used in the lobby.", "command.players.header": "=== Players ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "Your ping: {0}ms", - "narrator.meeting_only": "You can only use #narrator during a meeting.", - "narrator.disabled": "The narrator is currently disabled by the host.", - "narrator.status_enabled": "enabled", - "narrator.status_disabled": "disabled", - "narrator.host_status": "Status: {0} | Max uses per player per game: {1}", - "narrator.host_commands": "Host commands: #narrator enable | disable | limit ", - "narrator.host_enabled": "Narrator enabled for this game.", - "narrator.host_disabled": "Narrator disabled for this game.", - "narrator.host_limit_set": "Narrator usage limit set to {0} per player per game.", - "narrator.host_limit_zero": "Narrator usage limit set to 0 (effectively disabled).", - "narrator.host_limit_usage": "Usage: #narrator limit \nExample: #narrator limit 3", - "narrator.usage": "Usage: #narrator \nExample: #narrator I'm being accused but I have a visual task, how do I prove myself?" + "command.ping.result": "Your ping: {0}ms" } """; @@ -214,19 +228,7 @@ public sealed class LanguageService "command.players.not_in_lobby": "此指令只能在大厅中使用。", "command.players.header": "=== 在线玩家 ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "你的延迟:{0}ms", - "narrator.meeting_only": "只能在会议中使用 #narrator。", - "narrator.disabled": "房主已禁用旁白功能。", - "narrator.status_enabled": "已启用", - "narrator.status_disabled": "已禁用", - "narrator.host_status": "状态:{0} | 每局每人最大次数:{1}", - "narrator.host_commands": "房主指令:#narrator enable | disable | limit <次数>", - "narrator.host_enabled": "旁白已在此局中启用。", - "narrator.host_disabled": "旁白已在此局中禁用。", - "narrator.host_limit_set": "旁白使用次数限制已设为每人每局 {0} 次。", - "narrator.host_limit_zero": "旁白使用次数限制已设为 0(等效禁用)。", - "narrator.host_limit_usage": "用法:#narrator limit <次数>\n示例:#narrator limit 3", - "narrator.usage": "用法:#narrator <你要说的话>\n示例:#narrator 我被指控了但我有可视任务,怎么证明自己?" + "command.ping.result": "你的延迟:{0}ms" } """; @@ -265,19 +267,7 @@ public sealed class LanguageService "command.players.not_in_lobby": "此指令只能在大廳中使用。", "command.players.header": "=== 線上玩家 ({0}) ===", "command.players.entry": "{0} | {1} | {2}ms", - "command.ping.result": "你的延遲:{0}ms", - "narrator.meeting_only": "只能在會議中使用 #narrator。", - "narrator.disabled": "房主已停用旁白功能。", - "narrator.status_enabled": "已啟用", - "narrator.status_disabled": "已停用", - "narrator.host_status": "狀態:{0} | 每局每人最大次數:{1}", - "narrator.host_commands": "房主指令:#narrator enable | disable | limit <次數>", - "narrator.host_enabled": "旁白已在此局中啟用。", - "narrator.host_disabled": "旁白已在此局中停用。", - "narrator.host_limit_set": "旁白使用次數限制已設為每人每局 {0} 次。", - "narrator.host_limit_zero": "旁白使用次數限制已設為 0(等效停用)。", - "narrator.host_limit_usage": "用法:#narrator limit <次數>\n範例:#narrator limit 3", - "narrator.usage": "用法:#narrator <你要說的話>\n範例:#narrator 我被指控了但我有可視任務,怎麼證明自己?" + "command.ping.result": "你的延遲:{0}ms" } """; diff --git a/src/Empostor.Api/Plugins/IPluginLanguageProvider.cs b/src/Empostor.Api/Plugins/IPluginLanguageProvider.cs new file mode 100644 index 0000000..09ecb70 --- /dev/null +++ b/src/Empostor.Api/Plugins/IPluginLanguageProvider.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Empostor.Api.Plugins; + +/// +/// Implement this on your plugin class to provide translations that are +/// automatically registered with the language service on enable. +/// +public interface IPluginLanguageProvider +{ + /// + /// Returns translations grouped by language code. + /// + IReadOnlyDictionary> GetTranslations(); +} diff --git a/src/Empostor.Plugin.Narrator/NarratorPlugin.cs b/src/Empostor.Plugin.Narrator/NarratorPlugin.cs index b1ecc1a..19336f5 100644 --- a/src/Empostor.Plugin.Narrator/NarratorPlugin.cs +++ b/src/Empostor.Plugin.Narrator/NarratorPlugin.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Empostor.Api.Plugins; using Microsoft.Extensions.Logging; @@ -5,7 +6,7 @@ using Microsoft.Extensions.Logging; namespace Empostor.Plugin.Narrator; [EmpostorPlugin("cn.hayashiume.narrator")] -public sealed class NarratorPlugin : PluginBase +public sealed class NarratorPlugin : PluginBase, IPluginLanguageProvider { private readonly ILogger _logger; @@ -21,4 +22,56 @@ public sealed class NarratorPlugin : PluginBase } public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["narrator.meeting_only"] = "You can only use #narrator during a meeting.", + ["narrator.disabled"] = "The narrator is currently disabled by the host.", + ["narrator.status_enabled"] = "enabled", + ["narrator.status_disabled"] = "disabled", + ["narrator.host_status"] = "Status: {0} | Max uses per player per game: {1}", + ["narrator.host_commands"] = "Host commands: #narrator enable | disable | limit ", + ["narrator.host_enabled"] = "Narrator enabled for this game.", + ["narrator.host_disabled"] = "Narrator disabled for this game.", + ["narrator.host_limit_set"] = "Narrator usage limit set to {0} per player per game.", + ["narrator.host_limit_zero"] = "Narrator usage limit set to 0 (effectively disabled).", + ["narrator.host_limit_usage"] = "Usage: #narrator limit \nExample: #narrator limit 3", + ["narrator.usage"] = "Usage: #narrator \nExample: #narrator I'm being accused but I have a visual task, how do I prove myself?", + }, + ["zh_CN"] = new Dictionary + { + ["narrator.meeting_only"] = "只能在会议中使用 #narrator。", + ["narrator.disabled"] = "房主已禁用旁白功能。", + ["narrator.status_enabled"] = "已启用", + ["narrator.status_disabled"] = "已禁用", + ["narrator.host_status"] = "状态:{0} | 每局每人最大次数:{1}", + ["narrator.host_commands"] = "房主指令:#narrator enable | disable | limit <次数>", + ["narrator.host_enabled"] = "旁白已在此局中启用。", + ["narrator.host_disabled"] = "旁白已在此局中禁用。", + ["narrator.host_limit_set"] = "旁白使用次数限制已设为每人每局 {0} 次。", + ["narrator.host_limit_zero"] = "旁白使用次数限制已设为 0(等效禁用)。", + ["narrator.host_limit_usage"] = "用法:#narrator limit <次数>\n示例:#narrator limit 3", + ["narrator.usage"] = "用法:#narrator <你要说的话>\n示例:#narrator 我被指控了但我有可视任务,怎么证明自己?", + }, + ["zh_TW"] = new Dictionary + { + ["narrator.meeting_only"] = "只能在會議中使用 #narrator。", + ["narrator.disabled"] = "房主已停用旁白功能。", + ["narrator.status_enabled"] = "已啟用", + ["narrator.status_disabled"] = "已停用", + ["narrator.host_status"] = "狀態:{0} | 每局每人最大次數:{1}", + ["narrator.host_commands"] = "房主指令:#narrator enable | disable | limit <次數>", + ["narrator.host_enabled"] = "旁白已在此局中啟用。", + ["narrator.host_disabled"] = "旁白已在此局中停用。", + ["narrator.host_limit_set"] = "旁白使用次數限制已設為每人每局 {0} 次。", + ["narrator.host_limit_zero"] = "旁白使用次數限制已設為 0(等效停用)。", + ["narrator.host_limit_usage"] = "用法:#narrator limit <次數>\n範例:#narrator limit 3", + ["narrator.usage"] = "用法:#narrator <你要說的話>\n範例:#narrator 我被指控了但我有可視任務,怎麼證明自己?", + }, + }; + } } diff --git a/src/Empostor.Plugins.FriendCodeValidator/FriendCodeValidatorPlugin.cs b/src/Empostor.Plugins.FriendCodeValidator/FriendCodeValidatorPlugin.cs index ed0a212..fc601be 100644 --- a/src/Empostor.Plugins.FriendCodeValidator/FriendCodeValidatorPlugin.cs +++ b/src/Empostor.Plugins.FriendCodeValidator/FriendCodeValidatorPlugin.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Empostor.Api.Plugins; using Microsoft.Extensions.Logging; @@ -6,7 +7,7 @@ namespace Empostor.Plugins.FriendCodeValidator { // MatchDuck offered the Friendcode safenames.txt lol [EmpostorPlugin("duck.Empostor.friendcodevalidator", "Friend Code Validator", "MatchDuck & Empostor", "1.0.0")] - public sealed class FriendCodeValidatorPlugin : PluginBase + public sealed class FriendCodeValidatorPlugin : PluginBase, IPluginLanguageProvider { private readonly ILogger _logger; @@ -20,5 +21,24 @@ namespace Empostor.Plugins.FriendCodeValidator } public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["friendcode.invalid_reason"] = "Your friend code contains inappropriate words and cannot be used on this server.", + }, + ["zh_CN"] = new Dictionary + { + ["friendcode.invalid_reason"] = "您的好友代码包含不当词汇,无法在此服务器使用。", + }, + ["zh_TW"] = new Dictionary + { + ["friendcode.invalid_reason"] = "您的好友代碼包含不當詞彙,無法在此伺服器使用。", + }, + }; + } } } diff --git a/src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj b/src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj new file mode 100644 index 0000000..8d72e3e --- /dev/null +++ b/src/Empostor.Plugins.MapVote/Empostor.Plugins.MapVote.csproj @@ -0,0 +1,10 @@ + + + net8.0 + Empostor.Plugins.MapVote + enable + + + + + diff --git a/src/Empostor.Plugins.MapVote/MapVoteCommand.cs b/src/Empostor.Plugins.MapVote/MapVoteCommand.cs new file mode 100644 index 0000000..2a613d7 --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVoteCommand.cs @@ -0,0 +1,135 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using Empostor.Api.Commands; + +namespace Empostor.Plugins.MapVote; + +public sealed class MapVoteCommand : ICommand +{ + private readonly MapVoteService _service; + private readonly MapVoteConfig _config; + + public MapVoteCommand(MapVoteService service, MapVoteConfig config) + { + _service = service; + _config = config; + } + + public string Name => "votemap"; + public string[] Aliases => new[] { "vm" }; + public string Description => "Vote for the next map."; + public string Usage => "votemap "; + + public async ValueTask ExecuteAsync(CommandContext ctx) + { + var gameCode = ctx.Game.Code.ToString(); + + // Host control commands + if (ctx.Args.Length > 0) + { + var sub = ctx.Args[0].ToLowerInvariant(); + + if (sub == "enable" || sub == "on") + { + if (ctx.Sender.IsHost) + { + _service.SetEnabled(gameCode, true); + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.host_enabled", "Map voting enabled."), ctx.PlayerControl); + } + return true; + } + + if (sub == "disable" || sub == "off") + { + if (ctx.Sender.IsHost) + { + _service.SetEnabled(gameCode, false); + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.host_disabled", "Map voting disabled."), ctx.PlayerControl); + } + return true; + } + + if (sub == "results" || sub == "result") + { + await ShowResultsAsync(ctx); + return true; + } + } + + // Check if voting is enabled + if (!_service.IsEnabled(gameCode) && !ctx.Sender.IsHost) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.disabled", "Map voting is currently disabled by the host."), ctx.PlayerControl); + return true; + } + + // Voting requires a map argument + if (ctx.Args.Length == 0) + { + await ShowUsageAsync(ctx); + return true; + } + + if (!_service.TryParseMap(ctx.RawArgs!, out var map)) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.unknown_map", "Unknown map.").Replace("{0}", ctx.RawArgs ?? "?"), ctx.PlayerControl); + return true; + } + + var playerName = ctx.Sender.Character?.PlayerInfo?.PlayerName ?? ctx.Sender.Client.Name ?? "Unknown"; + _service.CastVote(gameCode, playerName, map); + + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.voted", "{0} voted for {1}.") + .Replace("{0}", playerName) + .Replace("{1}", MapVoteService.MapDisplayName(map)), + ctx.PlayerControl); + + return true; + } + + private async ValueTask ShowResultsAsync(CommandContext ctx) + { + var gameCode = ctx.Game.Code.ToString(); + var tally = _service.TallyVotes(gameCode); + + if (tally.Count == 0) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "mapvote.no_votes", "No map votes yet."), ctx.PlayerControl); + return; + } + + var sb = new StringBuilder(); + sb.AppendLine(T(ctx, "mapvote.results_header", "=== Map Vote Results ===")); + foreach (var (map, count) in tally) + { + sb.AppendLine(T(ctx, "mapvote.results_entry", " {0}: {1} vote(s)") + .Replace("{0}", MapVoteService.MapDisplayName(map)) + .Replace("{1}", count.ToString())); + } + + + await ctx.PlayerControl.SendChatToPlayerAsync(sb.ToString(), ctx.PlayerControl); + } + + private async ValueTask ShowUsageAsync(CommandContext ctx) + { + var sb = new StringBuilder(); + sb.AppendLine(T(ctx, "mapvote.usage", "Usage: #votemap ")); + if (ctx.Sender.IsHost) + sb.AppendLine(T(ctx, "mapvote.host_commands", "Host commands: ...")); + await ctx.PlayerControl.SendChatToPlayerAsync(sb.ToString(), ctx.PlayerControl); + } + + private static string T(CommandContext ctx, string key, string defaultText) + { + string result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? defaultText : result; + } +} diff --git a/src/Empostor.Plugins.MapVote/MapVoteConfig.cs b/src/Empostor.Plugins.MapVote/MapVoteConfig.cs new file mode 100644 index 0000000..739e3ab --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVoteConfig.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace Empostor.Plugins.MapVote; + +public sealed class MapVoteConfig +{ + [JsonPropertyName("enabled")] + public bool Enabled { get; set; } = true; + + [JsonPropertyName("require_majority")] + public bool RequireMajority { get; set; } = false; + + [JsonPropertyName("allow_host_override")] + public bool AllowHostOverride { get; set; } = true; +} diff --git a/src/Empostor.Plugins.MapVote/MapVoteEventListener.cs b/src/Empostor.Plugins.MapVote/MapVoteEventListener.cs new file mode 100644 index 0000000..49a27a2 --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVoteEventListener.cs @@ -0,0 +1,62 @@ +using System.Text; +using System.Threading.Tasks; +using Empostor.Api.Events; +using Empostor.Api.Events.Game; + +namespace Empostor.Plugins.MapVote; + +public sealed class MapVoteEventListener : IEventListener +{ + private readonly MapVoteService _service; + private readonly MapVoteConfig _config; + + public MapVoteEventListener(MapVoteService service, MapVoteConfig config) + { + _service = service; + _config = config; + } + + [EventListener] + public async ValueTask OnGameStarting(IGameStartingEvent e) + { + if (!_config.Enabled) return; + + var gameCode = e.Game.Code.ToString(); + + if (!_service.IsEnabled(gameCode)) return; + + var winner = _service.GetWinner(gameCode); + + // If host set a specific map and override is allowed, respect it + if (_config.AllowHostOverride && e.Game.Options.Map != winner) + { + var tally = _service.TallyVotes(gameCode); + if (tally.Count == 0) + { + // No votes at all — host's choice stands + _service.ResetVotes(gameCode); + return; + } + } + + e.Game.Options.Map = winner; + await e.Game.SyncSettingsAsync(); + + // Notify players + foreach (var player in e.Game.Players) + { + var ctrl = player.Character; + if (ctrl == null) continue; + await ctrl.SendChatToPlayerAsync( + $"Map set to {MapVoteService.MapDisplayName(winner)} by vote.", ctrl); + } + + _service.ResetVotes(gameCode); + } + + [EventListener] + public void OnGameDestroyed(IGameDestroyedEvent e) + { + _service.Remove(e.Game.Code.ToString()); + } +} diff --git a/src/Empostor.Plugins.MapVote/MapVotePlugin.cs b/src/Empostor.Plugins.MapVote/MapVotePlugin.cs new file mode 100644 index 0000000..64da178 --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVotePlugin.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Empostor.Api.Plugins; +using Microsoft.Extensions.Logging; + +namespace Empostor.Plugins.MapVote; + +[EmpostorPlugin("cn.hayashiume.mapvote")] +public sealed class MapVotePlugin : PluginBase, IPluginLanguageProvider +{ + private readonly ILogger _logger; + + public MapVotePlugin(ILogger logger) + { + _logger = logger; + } + + public override ValueTask EnableAsync() + { + _logger.LogInformation("[MapVote] Plugin enabled."); + return default; + } + + public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["mapvote.disabled"] = "Map voting is currently disabled by the host.", + ["mapvote.unknown_map"] = "Unknown map: {0}. Available: Skeld, Mira, Polus, Airship, Fungle", + ["mapvote.voted"] = "{0} voted for {1}.", + ["mapvote.already_voted"] = "You already voted for {0}. Use #votemap to change your vote.", + ["mapvote.no_votes"] = "No map votes yet. Use #votemap to vote!", + ["mapvote.results_header"] = "=== Map Vote Results ===", + ["mapvote.results_entry"] = " {0}: {1} vote(s)", + ["mapvote.results_winner"] = "Winner: {0} ({1} votes)", + ["mapvote.results_random"] = "No votes cast, using random map: {0}", + ["mapvote.usage"] = "Usage: #votemap \nMaps: Skeld, Mira, Polus, Airship, Fungle\nExample: #votemap polus", + ["mapvote.host_commands"] = "Host commands: #votemap enable | disable | results", + ["mapvote.host_enabled"] = "Map voting enabled.", + ["mapvote.host_disabled"] = "Map voting disabled.", + ["mapvote.map_set"] = "Map set to {0} by vote.", + }, + ["zh_CN"] = new Dictionary + { + ["mapvote.disabled"] = "投票选图功能已被房主禁用。", + ["mapvote.unknown_map"] = "未知地图:{0}。可选:Skeld, Mira, Polus, Airship, Fungle", + ["mapvote.voted"] = "{0} 投票选择了 {1}。", + ["mapvote.already_voted"] = "你已经投票了 {0}。使用 #votemap <地图> 来修改投票。", + ["mapvote.no_votes"] = "还没有人投票。使用 #votemap <地图> 来投票!", + ["mapvote.results_header"] = "=== 地图投票结果 ===", + ["mapvote.results_entry"] = " {0}:{1} 票", + ["mapvote.results_winner"] = "胜出:{0}({1} 票)", + ["mapvote.results_random"] = "无人投票,随机选择地图:{0}", + ["mapvote.usage"] = "用法:#votemap <地图>\n可选地图:Skeld, Mira, Polus, Airship, Fungle\n示例:#votemap polus", + ["mapvote.host_commands"] = "房主指令:#votemap enable | disable | results", + ["mapvote.host_enabled"] = "投票选图已启用。", + ["mapvote.host_disabled"] = "投票选图已禁用。", + ["mapvote.map_set"] = "投票结果为 {0}。", + }, + ["zh_TW"] = new Dictionary + { + ["mapvote.disabled"] = "投票選圖功能已被房主停用。", + ["mapvote.unknown_map"] = "未知地圖:{0}。可選:Skeld, Mira, Polus, Airship, Fungle", + ["mapvote.voted"] = "{0} 投票選擇了 {1}。", + ["mapvote.already_voted"] = "你已經投票了 {0}。使用 #votemap <地圖> 來修改投票。", + ["mapvote.no_votes"] = "還沒有人投票。使用 #votemap <地圖> 來投票!", + ["mapvote.results_header"] = "=== 地圖投票結果 ===", + ["mapvote.results_entry"] = " {0}:{1} 票", + ["mapvote.results_winner"] = "勝出:{0}({1} 票)", + ["mapvote.results_random"] = "無人投票,隨機選擇地圖:{0}", + ["mapvote.usage"] = "用法:#votemap <地圖>\n可選地圖:Skeld, Mira, Polus, Airship, Fungle\n範例:#votemap polus", + ["mapvote.host_commands"] = "房主指令:#votemap enable | disable | results", + ["mapvote.host_enabled"] = "投票選圖已啟用。", + ["mapvote.host_disabled"] = "投票選圖已停用。", + ["mapvote.map_set"] = "投票結果為 {0}。", + }, + }; + } +} diff --git a/src/Empostor.Plugins.MapVote/MapVoteService.cs b/src/Empostor.Plugins.MapVote/MapVoteService.cs new file mode 100644 index 0000000..6226ede --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVoteService.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using Empostor.Api.Innersloth; + +namespace Empostor.Plugins.MapVote; + +public sealed class MapVoteService +{ + private static readonly MapTypes[] AllMaps = + { + MapTypes.Skeld, MapTypes.MiraHQ, MapTypes.Polus, MapTypes.Airship, MapTypes.Fungle + }; + + private static readonly Dictionary MapAliases = new(StringComparer.OrdinalIgnoreCase) + { + ["skeld"] = MapTypes.Skeld, + ["the skeld"] = MapTypes.Skeld, + ["mira"] = MapTypes.MiraHQ, + ["mirahq"] = MapTypes.MiraHQ, + ["mira hq"] = MapTypes.MiraHQ, + ["polus"] = MapTypes.Polus, + ["airship"] = MapTypes.Airship, + ["fungle"] = MapTypes.Fungle, + // Chinese aliases + ["骷髅"] = MapTypes.Skeld, + ["米拉"] = MapTypes.MiraHQ, + ["波鲁斯"] = MapTypes.Polus, + ["飞船"] = MapTypes.Airship, + ["真菌"] = MapTypes.Fungle, + }; + + private readonly ConcurrentDictionary> _votes = new(); + private readonly ConcurrentDictionary _enabled = new(); + + public bool IsEnabled(string gameCode) => _enabled.GetValueOrDefault(gameCode, true); + + public void SetEnabled(string gameCode, bool enabled) => _enabled[gameCode] = enabled; + + public bool TryParseMap(string input, out MapTypes map) + { + if (MapAliases.TryGetValue(input.Trim(), out map)) + return true; + + if (int.TryParse(input.Trim(), out var id) && Enum.IsDefined(typeof(MapTypes), (byte)id)) + { + map = (MapTypes)(byte)id; + if (map != MapTypes.Dleks) // Exclude Dleks (mirrored Skeld) + return true; + } + + return false; + } + + public static string MapDisplayName(MapTypes map) => map switch + { + MapTypes.Skeld => "The Skeld", + MapTypes.MiraHQ => "Mira HQ", + MapTypes.Polus => "Polus", + MapTypes.Airship => "Airship", + MapTypes.Fungle => "Fungle", + _ => map.ToString(), + }; + + public void CastVote(string gameCode, string playerName, MapTypes map) + { + var gameVotes = _votes.GetOrAdd(gameCode, _ => new ConcurrentDictionary()); + gameVotes[playerName] = map; + } + + public MapTypes? GetPlayerVote(string gameCode, string playerName) + { + if (_votes.TryGetValue(gameCode, out var gameVotes) && gameVotes.TryGetValue(playerName, out var map)) + return map; + return null; + } + + public IReadOnlyDictionary TallyVotes(string gameCode) + { + if (!_votes.TryGetValue(gameCode, out var gameVotes) || gameVotes.IsEmpty) + return new Dictionary(); + + return gameVotes.Values + .GroupBy(m => m) + .ToDictionary(g => g.Key, g => g.Count()); + } + + public MapTypes GetWinner(string gameCode) + { + var tally = TallyVotes(gameCode); + if (tally.Count == 0) + { + var rng = new Random(); + return AllMaps[rng.Next(AllMaps.Length)]; + } + + var maxVotes = tally.Values.Max(); + var winners = tally.Where(kv => kv.Value == maxVotes).Select(kv => kv.Key).ToList(); + + if (winners.Count == 1) + return winners[0]; + + var rng2 = new Random(); + return winners[rng2.Next(winners.Count)]; + } + + public void ResetVotes(string gameCode) + { + _votes.TryRemove(gameCode, out _); + } + + internal void Remove(string gameCode) + { + _votes.TryRemove(gameCode, out _); + _enabled.TryRemove(gameCode, out _); + } +} diff --git a/src/Empostor.Plugins.MapVote/MapVoteStartup.cs b/src/Empostor.Plugins.MapVote/MapVoteStartup.cs new file mode 100644 index 0000000..cfbc500 --- /dev/null +++ b/src/Empostor.Plugins.MapVote/MapVoteStartup.cs @@ -0,0 +1,26 @@ +using Empostor.Api.Commands; +using Empostor.Api.Events; +using Empostor.Api.Plugins; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Empostor.Plugins.MapVote; + +public sealed class MapVoteStartup : IPluginStartup +{ + private string _configPath = "config"; + + public void SetConfigPath(string configPath) => _configPath = configPath; + + public void ConfigureHost(IHostBuilder host) { } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(_ => PluginConfigLoader.Load(_configPath)); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + } +} diff --git a/src/Empostor.Plugins.MapVote/packages.lock.json b/src/Empostor.Plugins.MapVote/packages.lock.json new file mode 100644 index 0000000..ac11d80 --- /dev/null +++ b/src/Empostor.Plugins.MapVote/packages.lock.json @@ -0,0 +1,137 @@ +{ + "version": 1, + "dependencies": { + "net8.0": { + "BouncyCastle.Cryptography": { + "type": "Transitive", + "resolved": "2.6.1", + "contentHash": "vZsG2YILhthgRqO+ZVgRff4ZFKKTl0v7kqaVBLCtRvpREhfBP33pcWrdA3PRYgWuFL1RxiUFvjMUHTdBZlJcoA==" + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "Nxs7Z1q3f1STfLYKJSVXCs1iBl+Ya6E8o4Oy1bCxJ/rNI44E/0f6tbsrVqAWfB7jlnJfyaAtIalBVxPKUPQb4Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.2.0", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.2.0", + "contentHash": "ziFz5zH8f33En4dX81LW84I6XrYXKf9jg6aM39cM+LffN9KJahViKZ61dGMSO2gd3e+qe5yBRwsesvyqlZaSMg==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.2.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "9.0.6", + "contentHash": "0Zn6nR/6g+90MxskZyOOMPQvnPnrrGu6bytPwkV+azDcTtCSuQ1+GJUrg8Klmnrjk1i6zMpw2lXijl+tw7Q3kA==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0", + "System.Diagnostics.DiagnosticSource": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "arDBqTgFCyS0EvRV7O3MZturChstm50OJ0y9bDJvAcmEPJm0FFpFyjU/JLYyStNGGey081DvnQYlncNX5SJJGA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "X21b8D6z58EG34OMLH955750mFaEaGs5xLGe9Vyi3059Cgb3sk+p2k/JlQXaieEBRrk+SVlFzDei3Q6PdVzJOA==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JOVOfqpnqlVLUzINQ2fox8evY2SKLYJ3BV8QDe/Jyp21u1T7r45x/R/5QdteURMR5r01GxeJSBBUOCOyaNXA3g==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "4.3.1-dev-02373", + "contentHash": "f0HNPaleOox5++8zqxAdi9afnUUkoznLOmd0ur/UnLbzl8bIaPyyBGOpbIJmC1kz2vHfkQ0fXh6KXMdlz7bcLQ==" + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "c9xLpVz6PL9lp/djOWtk5KPDZq3cSYpmXoJQY524EOtuFl5z9ZtsotpsyrDW40U1DRnQSYvcPKEUV0X//u6gkQ==" + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Xg4G4Indi4dqP1iuAiMSwpiWS54ZghzR644OtsRCm/m/lBMG8dUBhLVN7hLm8NNrNTR+iGbshCPTwrvxZPlm4g==" + }, + "empostor.api": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "[2.2.0, )", + "Microsoft.Extensions.Hosting.Abstractions": "[8.0.0, )", + "Microsoft.Extensions.Logging.Abstractions": "[8.0.0, )", + "NextFast.Hazel": "[1.0.2-dev, )", + "NextFast.Hazel.Abstractions": "[1.0.2-dev, )" + } + }, + "NextFast.Hazel": { + "type": "Project", + "dependencies": { + "BouncyCastle.Cryptography": "[2.6.1, )", + "Microsoft.Extensions.DependencyInjection.Abstractions": "[9.0.6, )", + "Microsoft.Extensions.ObjectPool": "[8.0.1, )", + "NextFast.Hazel.Abstractions": "[1.0.2-dev, )", + "Serilog": "[4.3.1-dev-02373, )" + } + }, + "NextFast.Hazel.Abstractions": { + "type": "Project" + } + } + } +} \ No newline at end of file diff --git a/src/Empostor.Plugins.Message/Empostor.Plugins.Message.csproj b/src/Empostor.Plugins.Message/Empostor.Plugins.Message.csproj new file mode 100644 index 0000000..b7d13e2 --- /dev/null +++ b/src/Empostor.Plugins.Message/Empostor.Plugins.Message.csproj @@ -0,0 +1,10 @@ + + + net8.0 + Empostor.Plugins.Message + enable + + + + + diff --git a/src/Empostor.Plugins.Message/MessageCommand.cs b/src/Empostor.Plugins.Message/MessageCommand.cs new file mode 100644 index 0000000..ca061b1 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessageCommand.cs @@ -0,0 +1,109 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using Empostor.Api.Commands; + +namespace Empostor.Plugins.Message; + +public sealed class MessageCommand : ICommand +{ + private readonly MessageStore _store; + private readonly MessageConfig _config; + + public MessageCommand(MessageStore store, MessageConfig config) + { + _store = store; + _config = config; + } + + public string Name => "msg"; + public string[] Aliases => new[] { "message", "leave" }; + public string Description => "Leave a message for a player by friend code."; + public string Usage => "msg "; + + public async ValueTask ExecuteAsync(CommandContext ctx) + { + if (ctx.Args.Length < 2) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.usage", "Usage: #msg "), ctx.PlayerControl); + return true; + } + + var targetFc = ctx.Args[0].Trim().ToUpperInvariant(); + var content = ctx.RawArgs!; + + // Remove the friend code prefix from the raw args to get the message content + var contentStart = ctx.RawArgs!.IndexOf(targetFc, StringComparison.OrdinalIgnoreCase); + if (contentStart >= 0) + content = ctx.RawArgs[(contentStart + ctx.Args[0].Length)..].Trim(); + + if (string.IsNullOrWhiteSpace(content)) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.empty", "Message cannot be empty."), ctx.PlayerControl); + return true; + } + + // Validate friend code format + if (targetFc.Length < 4) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.invalid_fc", "Invalid friend code format."), ctx.PlayerControl); + return true; + } + + var senderFc = ctx.Sender.Client.FriendCode ?? ""; + if (string.Equals(senderFc, targetFc, StringComparison.OrdinalIgnoreCase)) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.self", "You cannot send a message to yourself."), ctx.PlayerControl); + return true; + } + + if (content.Length > _config.MessageMaxLength) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.too_long", "Message is too long.") + .Replace("{0}", _config.MessageMaxLength.ToString()), + ctx.PlayerControl); + return true; + } + + if (_store.Count(targetFc) >= _config.MaxMessagesPerTarget) + { + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.full", "Already has too many pending messages.") + .Replace("{0}", targetFc), + ctx.PlayerControl); + return true; + } + + var senderName = ctx.Sender.Character?.PlayerInfo?.PlayerName + ?? ctx.Sender.Client.Name + ?? "Unknown"; + + var msg = new PendingMessage + { + SenderName = senderName, + SenderFc = senderFc, + TargetFc = targetFc, + Content = content, + Timestamp = DateTime.UtcNow, + }; + + _store.Add(msg); + + await ctx.PlayerControl.SendChatToPlayerAsync( + T(ctx, "message.sent", "Message sent to {0}.").Replace("{0}", targetFc), + ctx.PlayerControl); + + return true; + } + + private static string T(CommandContext ctx, string key, string defaultText) + { + string result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? defaultText : result; + } +} diff --git a/src/Empostor.Plugins.Message/MessageConfig.cs b/src/Empostor.Plugins.Message/MessageConfig.cs new file mode 100644 index 0000000..eabd939 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessageConfig.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace Empostor.Plugins.Message; + +public sealed class MessageConfig +{ + [JsonPropertyName("max_messages_per_target")] + public int MaxMessagesPerTarget { get; set; } = 10; + + [JsonPropertyName("message_max_length")] + public int MessageMaxLength { get; set; } = 500; +} diff --git a/src/Empostor.Plugins.Message/MessageEventListener.cs b/src/Empostor.Plugins.Message/MessageEventListener.cs new file mode 100644 index 0000000..0354673 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessageEventListener.cs @@ -0,0 +1,71 @@ +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Empostor.Api.Events; +using Empostor.Api.Events.Client; +using Empostor.Api.Events.Game; + +namespace Empostor.Plugins.Message; + +public sealed class MessageEventListener : IEventListener +{ + private readonly MessageStore _store; + + public MessageEventListener(MessageStore store) + { + _store = store; + } + + [EventListener] + public async ValueTask OnClientConnected(IClientConnectedEvent e) + { + await DeliverMessages(e.Client.FriendCode, e.Client.Language, null); + } + + [EventListener] + public async ValueTask OnPlayerJoined(IGamePlayerJoinedEvent e) + { + var fc = e.Player.Client.FriendCode; + if (string.IsNullOrEmpty(fc)) return; + + // Deliver any undelivered messages when player joins a game + var messages = _store.TakeAll(fc); + if (messages.Count == 0) return; + + var ctrl = e.Player.Character; + if (ctrl == null) return; + + await DeliverToPlayer(messages, ctrl, e.Player.Client.Name ?? "Player"); + } + + private async ValueTask DeliverMessages(string? friendCode, Api.Languages.Language language, Api.Innersloth.IInnerPlayerControl? ctrl) + { + if (string.IsNullOrEmpty(friendCode)) return; + + var messages = _store.TakeAll(friendCode); + if (messages.Count == 0) return; + + // Messages were already delivered via OnPlayerJoined with character reference + // OnClientConnected happens before player has a character, so store them back and wait for game join + foreach (var msg in messages) + _store.Add(msg); + } + + private async ValueTask DeliverToPlayer( + System.Collections.Generic.IReadOnlyList messages, + Empostor.Api.Innersloth.IInnerPlayerControl ctrl, + string playerName) + { + var sb = new StringBuilder(); + sb.AppendLine(string.Format("--- You have {0} pending message(s) ---", messages.Count)); + + foreach (var msg in messages.OrderBy(m => m.Timestamp)) + { + var time = msg.Timestamp.ToLocalTime().ToString("MM-dd HH:mm"); + sb.AppendLine($"[{time}] <{msg.SenderName}> {msg.Content}"); + } + + await ctrl.SendChatToPlayerAsync(sb.ToString(), ctrl); + } +} diff --git a/src/Empostor.Plugins.Message/MessagePlugin.cs b/src/Empostor.Plugins.Message/MessagePlugin.cs new file mode 100644 index 0000000..4f9be35 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessagePlugin.cs @@ -0,0 +1,68 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Empostor.Api.Plugins; +using Microsoft.Extensions.Logging; + +namespace Empostor.Plugins.Message; + +[EmpostorPlugin("cn.hayashiume.message")] +public sealed class MessagePlugin : PluginBase, IPluginLanguageProvider +{ + private readonly ILogger _logger; + + public MessagePlugin(ILogger logger) + { + _logger = logger; + } + + public override ValueTask EnableAsync() + { + _logger.LogInformation("[Message] Plugin enabled."); + return default; + } + + public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["message.usage"] = "Usage: #msg \nExample: #msg ABCDEFG Hey, let's play again!", + ["message.sent"] = "Message sent to {0}.", + ["message.too_long"] = "Message is too long. Max {0} characters.", + ["message.empty"] = "Message cannot be empty.", + ["message.invalid_fc"] = "Invalid friend code format.", + ["message.self"] = "You cannot send a message to yourself.", + ["message.full"] = "{0} already has too many pending messages. Try again later.", + ["message.delivery_header"] = "--- You have {0} pending message(s) ---", + ["message.delivery_entry"] = "[{0}] <{1}> {2}: {3}", + }, + ["zh_CN"] = new Dictionary + { + ["message.usage"] = "用法:#msg <好友代码> <留言内容>\n示例:#msg ABCDEFG 下次一起玩!", + ["message.sent"] = "留言已发送给 {0}。", + ["message.too_long"] = "留言内容过长,最多 {0} 个字符。", + ["message.empty"] = "留言内容不能为空。", + ["message.invalid_fc"] = "好友代码格式无效。", + ["message.self"] = "不能给自己留言。", + ["message.full"] = "{0} 的待收留言已满,请稍后再试。", + ["message.delivery_header"] = "--- 你有 {0} 条待收留言 ---", + ["message.delivery_entry"] = "[{0}] <{1}> {2}: {3}", + }, + ["zh_TW"] = new Dictionary + { + ["message.usage"] = "用法:#msg <好友代碼> <留言內容>\n範例:#msg ABCDEFG 下次一起玩!", + ["message.sent"] = "留言已發送給 {0}。", + ["message.too_long"] = "留言內容過長,最多 {0} 個字元。", + ["message.empty"] = "留言內容不能為空。", + ["message.invalid_fc"] = "好友代碼格式無效。", + ["message.self"] = "不能給自己留言。", + ["message.full"] = "{0} 的待收留言已滿,請稍後再試。", + ["message.delivery_header"] = "--- 你有 {0} 條待收留言 ---", + ["message.delivery_entry"] = "[{0}] <{1}> {2}: {3}", + }, + }; + } +} diff --git a/src/Empostor.Plugins.Message/MessageStartup.cs b/src/Empostor.Plugins.Message/MessageStartup.cs new file mode 100644 index 0000000..d5372c0 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessageStartup.cs @@ -0,0 +1,26 @@ +using Empostor.Api.Commands; +using Empostor.Api.Events; +using Empostor.Api.Plugins; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Empostor.Plugins.Message; + +public sealed class MessageStartup : IPluginStartup +{ + private string _configPath = "config"; + + public void SetConfigPath(string configPath) => _configPath = configPath; + + public void ConfigureHost(IHostBuilder host) { } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(_ => PluginConfigLoader.Load(_configPath)); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + } +} diff --git a/src/Empostor.Plugins.Message/MessageStore.cs b/src/Empostor.Plugins.Message/MessageStore.cs new file mode 100644 index 0000000..8447ff5 --- /dev/null +++ b/src/Empostor.Plugins.Message/MessageStore.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.Json; +using Microsoft.Extensions.Logging; + +namespace Empostor.Plugins.Message; + +public sealed class MessageStore +{ + private readonly ConcurrentDictionary> _messages = new(StringComparer.OrdinalIgnoreCase); + private readonly ILogger _logger; + private readonly string _filePath; + private readonly object _saveLock = new(); + + public MessageStore(ILogger logger, MessageConfig config) + { + _logger = logger; + _filePath = Path.Combine(Directory.GetCurrentDirectory(), "config", "messages.json"); + Load(); + } + + public int Count(string targetFc) + { + if (_messages.TryGetValue(targetFc, out var list)) + return list.Count; + return 0; + } + + public void Add(PendingMessage msg) + { + var list = _messages.GetOrAdd(msg.TargetFc, _ => new List()); + lock (list) + { + list.Add(msg); + } + Save(); + } + + public IReadOnlyList TakeAll(string targetFc) + { + if (!_messages.TryGetValue(targetFc, out var list) || list.Count == 0) + return Array.Empty(); + + List taken; + lock (list) + { + taken = list.ToList(); + list.Clear(); + } + + _messages.TryRemove(targetFc, out _); + Save(); + return taken; + } + + private void Load() + { + try + { + var dir = Path.GetDirectoryName(_filePath); + if (dir != null && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + if (!File.Exists(_filePath)) + return; + + var json = File.ReadAllText(_filePath); + var data = JsonSerializer.Deserialize>>(json); + if (data == null) return; + + foreach (var (fc, msgs) in data) + { + if (msgs.Count > 0) + _messages[fc] = msgs; + } + + var total = _messages.Values.Sum(v => v.Count); + _logger.LogInformation("[MessageStore] Loaded {Count} pending messages.", total); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "[MessageStore] Failed to load messages."); + } + } + + private void Save() + { + lock (_saveLock) + { + try + { + var dir = Path.GetDirectoryName(_filePath); + if (dir != null && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + var data = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var (fc, list) in _messages) + { + List copy; + lock (list) + { + copy = list.ToList(); + } + + if (copy.Count > 0) + data[fc] = copy; + } + + var json = JsonSerializer.Serialize(data, new JsonSerializerOptions { WriteIndented = true }); + File.WriteAllText(_filePath, json); + } + catch (Exception ex) + { + _logger.LogWarning(ex, "[MessageStore] Failed to save messages."); + } + } + } +} diff --git a/src/Empostor.Plugins.Message/PendingMessage.cs b/src/Empostor.Plugins.Message/PendingMessage.cs new file mode 100644 index 0000000..a1e26d9 --- /dev/null +++ b/src/Empostor.Plugins.Message/PendingMessage.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.Json.Serialization; + +namespace Empostor.Plugins.Message; + +public sealed class PendingMessage +{ + [JsonPropertyName("id")] + public string Id { get; set; } = Guid.NewGuid().ToString("N"); + + [JsonPropertyName("sender_name")] + public string SenderName { get; set; } = ""; + + [JsonPropertyName("sender_fc")] + public string SenderFc { get; set; } = ""; + + [JsonPropertyName("target_fc")] + public string TargetFc { get; set; } = ""; + + [JsonPropertyName("content")] + public string Content { get; set; } = ""; + + [JsonPropertyName("timestamp")] + public DateTime Timestamp { get; set; } = DateTime.UtcNow; +} diff --git a/src/Empostor.Plugins.PlayerChannel/PlayerChannelCommand.cs b/src/Empostor.Plugins.PlayerChannel/PlayerChannelCommand.cs index b57b60e..c9cda29 100644 --- a/src/Empostor.Plugins.PlayerChannel/PlayerChannelCommand.cs +++ b/src/Empostor.Plugins.PlayerChannel/PlayerChannelCommand.cs @@ -38,18 +38,22 @@ public sealed class PlayerChannelCommand : ICommand if (string.IsNullOrEmpty(senderFc)) { await ctx.PlayerControl.SendChatToPlayerAsync( - "[Refuse Channel] Unnkown Friendcode", ctx.PlayerControl); + T(ctx, "playerchannel.unknown_friendcode", "[Refuse Channel] Unknown Friendcode"), + ctx.PlayerControl); return true; } if (!_fcToChannel.TryGetValue(senderFc, out var channel)) { await ctx.PlayerControl.SendChatToPlayerAsync( - "[Refuse Channel] No exsit in any Channnel", ctx.PlayerControl); + T(ctx, "playerchannel.not_in_channel", "[Refuse Channel] Not in any channel"), + ctx.PlayerControl); return true; } - var prefixed = $"[{channel.Name}] {text}"; + var prefixed = T(ctx, "playerchannel.message_format", "[{0}] {1}") + .Replace("{0}", channel.Name) + .Replace("{1}", text); foreach (var player in ctx.Game.Players) { @@ -65,4 +69,10 @@ public sealed class PlayerChannelCommand : ICommand return true; } + + private static string T(CommandContext ctx, string key, string defaultText) + { + string result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? defaultText : result; + } } diff --git a/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs b/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs index d38d359..5e74294 100644 --- a/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs +++ b/src/Empostor.Plugins.PlayerChannel/PlayerChannelPlugin.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Empostor.Api.Plugins; using Microsoft.Extensions.Logging; @@ -5,7 +6,7 @@ using Microsoft.Extensions.Logging; namespace Empostor.Plugins.PlayerChannel; [EmpostorPlugin("cn.hayashiume.playerchannel", "Player Channel", "HayashiUme", "1.0.0")] -public sealed class PlayerChannelPlugin : PluginBase +public sealed class PlayerChannelPlugin : PluginBase, IPluginLanguageProvider { private readonly ILogger _logger; @@ -21,4 +22,29 @@ public sealed class PlayerChannelPlugin : PluginBase } public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["playerchannel.unknown_friendcode"] = "[Refuse Channel] Unknown Friendcode", + ["playerchannel.not_in_channel"] = "[Refuse Channel] Not in any channel", + ["playerchannel.message_format"] = "[{0}] {1}", + }, + ["zh_CN"] = new Dictionary + { + ["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代码", + ["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何频道中", + ["playerchannel.message_format"] = "[{0}] {1}", + }, + ["zh_TW"] = new Dictionary + { + ["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代碼", + ["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何頻道中", + ["playerchannel.message_format"] = "[{0}] {1}", + }, + }; + } } diff --git a/src/Empostor.Plugins.QqVerify/QqVerifyCommand.cs b/src/Empostor.Plugins.QqVerify/QqVerifyCommand.cs index 4b0809b..e130af8 100644 --- a/src/Empostor.Plugins.QqVerify/QqVerifyCommand.cs +++ b/src/Empostor.Plugins.QqVerify/QqVerifyCommand.cs @@ -26,7 +26,9 @@ public sealed class QqVerifyCommand : ICommand if (!ctx.IsSenderChinese()) { await ctx.PlayerControl.SendChatToPlayerAsync( - "此功能仅支持简体/繁体中文玩家使用。\nThis command is only available for Chinese language players.", ctx.PlayerControl); + T(ctx, "qqverify.chinese_only", + "This command is only available for Chinese language players.\n此功能仅支持简体/繁体中文玩家使用。"), + ctx.PlayerControl); return true; } @@ -34,7 +36,9 @@ public sealed class QqVerifyCommand : ICommand if (string.IsNullOrEmpty(fc)) { await ctx.PlayerControl.SendChatToPlayerAsync( - "无法获取你的好友代码,请确保已登录账号。", ctx.PlayerControl); + T(ctx, "qqverify.no_friendcode", + "Unable to get your friend code. Please make sure you are logged in."), + ctx.PlayerControl); return true; } @@ -42,15 +46,26 @@ public sealed class QqVerifyCommand : ICommand if (string.IsNullOrEmpty(qqNumber) || !long.TryParse(qqNumber, out _)) { await ctx.PlayerControl.SendChatToPlayerAsync( - "用法:/verify <你的QQ号>\n示例:/verify 123456789", ctx.PlayerControl); + T(ctx, "qqverify.usage_message", + "Usage: #verify \nExample: #verify 123456789"), + ctx.PlayerControl); return true; } _store.AddPending(fc, qqNumber); await ctx.PlayerControl.SendChatToPlayerAsync( - $"已记录验证请求!请私聊QQ机器人发送:/验证 {fc}\n注意:验证码10分钟内有效。", ctx.PlayerControl); + T(ctx, "qqverify.recorded", + "Verification request recorded! Send /验证 {0} to the QQ bot.\nNote: The code is valid for 10 minutes.") + .Replace("{0}", fc), + ctx.PlayerControl); return true; } + + private static string T(CommandContext ctx, string key, string defaultText) + { + string result = ctx.Lang.Get(key, ctx.SenderLanguage); + return result == key ? defaultText : result; + } } diff --git a/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs b/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs index c529a1f..5fa5646 100644 --- a/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs +++ b/src/Empostor.Plugins.QqVerify/QqVerifyPlugin.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Empostor.Api.Plugins; using Microsoft.Extensions.Logging; @@ -5,7 +6,7 @@ using Microsoft.Extensions.Logging; namespace Empostor.Plugins.QqVerify; [EmpostorPlugin("cn.hayashiume.qqverify", "QQ Verify", "ELinmei", "1.0.0")] -public sealed class QqVerifyPlugin : PluginBase +public sealed class QqVerifyPlugin : PluginBase, IPluginLanguageProvider { private readonly ILogger _logger; @@ -16,9 +17,37 @@ public sealed class QqVerifyPlugin : PluginBase public override ValueTask EnableAsync() { - _logger.LogInformation("[QQVerify] Plugin enabled. /verify command registered."); + _logger.LogInformation("[QQVerify] Plugin enabled. #verify command registered."); return default; } public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["qqverify.chinese_only"] = "This command is only available for Chinese language players.\n此功能仅支持简体/繁体中文玩家使用。", + ["qqverify.no_friendcode"] = "Unable to get your friend code. Please make sure you are logged in.", + ["qqverify.usage_message"] = "Usage: #verify \nExample: #verify 123456789", + ["qqverify.recorded"] = "Verification request recorded! Send /验证 {0} to the QQ bot.\nNote: The code is valid for 10 minutes.", + }, + ["zh_CN"] = new Dictionary + { + ["qqverify.chinese_only"] = "此功能仅支持简体/繁体中文玩家使用。\nThis command is only available for Chinese language players.", + ["qqverify.no_friendcode"] = "无法获取你的好友代码,请确保已登录账号。", + ["qqverify.usage_message"] = "用法:#verify <你的QQ号>\n示例:#verify 123456789", + ["qqverify.recorded"] = "已记录验证请求!请私聊QQ机器人发送:/验证 {0}\n注意:验证码10分钟内有效。", + }, + ["zh_TW"] = new Dictionary + { + ["qqverify.chinese_only"] = "此功能僅支援簡體/繁體中文玩家使用。\nThis command is only available for Chinese language players.", + ["qqverify.no_friendcode"] = "無法獲取你的好友代碼,請確保已登入帳號。", + ["qqverify.usage_message"] = "用法:#verify <你的QQ號>\n範例:#verify 123456789", + ["qqverify.recorded"] = "已記錄驗證請求!請私聊QQ機器人發送:/驗證 {0}\n注意:驗證碼10分鐘內有效。", + }, + }; + } } diff --git a/src/Empostor.Plugins.Titles/TitlesPlugin.cs b/src/Empostor.Plugins.Titles/TitlesPlugin.cs index 8625c43..5a1f709 100644 --- a/src/Empostor.Plugins.Titles/TitlesPlugin.cs +++ b/src/Empostor.Plugins.Titles/TitlesPlugin.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Empostor.Api.Plugins; using Microsoft.Extensions.Logging; @@ -5,7 +6,7 @@ using Microsoft.Extensions.Logging; namespace Empostor.Plugins.Titles; [EmpostorPlugin("cn.Empostor.titles", "Title System", "Empostor", "1.0.0")] -public sealed class TitlesPlugin : PluginBase +public sealed class TitlesPlugin : PluginBase, IPluginLanguageProvider { private readonly ILogger _logger; @@ -18,4 +19,23 @@ public sealed class TitlesPlugin : PluginBase } public override ValueTask DisableAsync() => default; + + public IReadOnlyDictionary> GetTranslations() + { + return new Dictionary> + { + ["en"] = new Dictionary + { + ["titles.display_format"] = "[{0}] {1}", + }, + ["zh_CN"] = new Dictionary + { + ["titles.display_format"] = "[{0}] {1}", + }, + ["zh_TW"] = new Dictionary + { + ["titles.display_format"] = "[{0}] {1}", + }, + }; + } } diff --git a/src/Empostor.Server/Http/MonitorController.cs b/src/Empostor.Server/Http/MonitorController.cs new file mode 100644 index 0000000..3b401d3 --- /dev/null +++ b/src/Empostor.Server/Http/MonitorController.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text.Json.Serialization; +using Empostor.Api.Games.Managers; +using Empostor.Api.Net.Manager; +using Microsoft.AspNetCore.Mvc; + +namespace Empostor.Server.Http; + +[ApiController] +public sealed class MonitorController : ControllerBase +{ + private static readonly DateTime StartTime = Process.GetCurrentProcess().StartTime.ToUniversalTime(); + + private readonly IGameManager _gameManager; + private readonly IClientManager _clientManager; + + public MonitorController(IGameManager gameManager, IClientManager clientManager) + { + _gameManager = gameManager; + _clientManager = clientManager; + } + + [HttpGet("/api/monitor/status")] + public IActionResult GetStatus() + { + var process = Process.GetCurrentProcess(); + var uptime = DateTime.UtcNow - StartTime; + + var games = _gameManager.Games.ToList(); + var clients = _clientManager.Clients.ToList(); + + var status = new MonitorStatus + { + Status = "running", + Timestamp = DateTime.UtcNow.ToString("o"), + UptimeSeconds = (long)uptime.TotalSeconds, + UptimeDisplay = $"{uptime.Days}d {uptime.Hours}h {uptime.Minutes}m", + Version = GetVersion(), + Runtime = RuntimeInformation.FrameworkDescription, + Platform = RuntimeInformation.OSDescription, + Processors = Environment.ProcessorCount, + MemoryMb = process.WorkingSet64 / 1024 / 1024, + GameCount = games.Count, + PlayerCount = clients.Count(c => c.Player != null), + ActiveConnections = clients.Count, + Games = games.Select(g => new MonitorGame + { + Code = g.Code.ToString(), + State = g.GameState.ToString(), + Map = g.Options.Map.ToString(), + PlayerCount = g.PlayerCount, + Host = g.Host?.Character?.PlayerInfo?.PlayerName ?? "(none)", + }).ToList(), + }; + + return new JsonResult(status); + } + + [HttpGet("/api/monitor/health")] + public IActionResult GetHealth() + { + return Ok(new { status = "healthy", timestamp = DateTime.UtcNow.ToString("o") }); + } + + private static string GetVersion() + { + try + { + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var info = System.Diagnostics.FileVersionInfo.GetVersionInfo(assembly.Location); + return info.ProductVersion ?? "unknown"; + } + catch + { + return "unknown"; + } + } +} + +public sealed class MonitorStatus +{ + [JsonPropertyName("status")] + public string Status { get; set; } = ""; + + [JsonPropertyName("timestamp")] + public string Timestamp { get; set; } = ""; + + [JsonPropertyName("uptime_seconds")] + public long UptimeSeconds { get; set; } + + [JsonPropertyName("uptime_display")] + public string UptimeDisplay { get; set; } = ""; + + [JsonPropertyName("version")] + public string Version { get; set; } = ""; + + [JsonPropertyName("runtime")] + public string Runtime { get; set; } = ""; + + [JsonPropertyName("platform")] + public string Platform { get; set; } = ""; + + [JsonPropertyName("processors")] + public int Processors { get; set; } + + [JsonPropertyName("memory_mb")] + public long MemoryMb { get; set; } + + [JsonPropertyName("game_count")] + public int GameCount { get; set; } + + [JsonPropertyName("player_count")] + public int PlayerCount { get; set; } + + [JsonPropertyName("active_connections")] + public int ActiveConnections { get; set; } + + [JsonPropertyName("games")] + public List Games { get; set; } = new(); +} + +public sealed class MonitorGame +{ + [JsonPropertyName("code")] + public string Code { get; set; } = ""; + + [JsonPropertyName("state")] + public string State { get; set; } = ""; + + [JsonPropertyName("map")] + public string Map { get; set; } = ""; + + [JsonPropertyName("player_count")] + public int PlayerCount { get; set; } + + [JsonPropertyName("host")] + public string Host { get; set; } = ""; +} diff --git a/src/Empostor.Server/Http/PrivacyController.cs b/src/Empostor.Server/Http/PrivacyController.cs new file mode 100644 index 0000000..715109d --- /dev/null +++ b/src/Empostor.Server/Http/PrivacyController.cs @@ -0,0 +1,135 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; + +namespace Empostor.Server.Http; + +[ApiController] +[Route("/privacy")] +public sealed class PrivacyController : ControllerBase +{ + private static readonly string PagesDir = Path.Combine(Directory.GetCurrentDirectory(), "Pages"); + private static readonly string PrivacyFile = Path.Combine(PagesDir, "privacy.html"); + private static bool _initialized; + private static readonly object _initLock = new(); + + private readonly ILogger _logger; + + public PrivacyController(ILogger logger) + { + _logger = logger; + EnsureFile(); + } + + [HttpGet] + public IActionResult GetPrivacy() + { + if (!System.IO.File.Exists(PrivacyFile)) + return Content(DefaultPrivacyHtml(), "text/html; charset=utf-8"); + + var html = System.IO.File.ReadAllText(PrivacyFile); + return Content(html, "text/html; charset=utf-8"); + } + + [HttpPost] + [Route("/admin/api/privacy")] + public async Task UpdatePrivacy() + { + using var reader = new StreamReader(Request.Body); + var body = await reader.ReadToEndAsync(); + + try + { + var doc = System.Text.Json.JsonDocument.Parse(body); + var content = doc.RootElement.GetProperty("content").GetString() ?? ""; + var token = doc.RootElement.GetProperty("token").GetString() ?? ""; + + var adminToken = Environment.GetEnvironmentVariable("EMP_HTTP_TOKEN") + ?? Environment.GetEnvironmentVariable("EMP_ADMIN_TOKEN") + ?? "empostor"; + + if (token != adminToken) + return Unauthorized(new { error = "Invalid token." }); + + System.IO.File.WriteAllText(PrivacyFile, content); + _logger.LogInformation("[PrivacyController] Privacy policy updated."); + return Ok(new { success = true }); + } + catch (Exception ex) + { + return BadRequest(new { error = ex.Message }); + } + } + + private void EnsureFile() + { + if (_initialized) return; + + lock (_initLock) + { + if (_initialized) return; + _initialized = true; + + if (!Directory.Exists(PagesDir)) + Directory.CreateDirectory(PagesDir); + + if (!System.IO.File.Exists(PrivacyFile)) + { + System.IO.File.WriteAllText(PrivacyFile, DefaultPrivacyHtml()); + _logger.LogInformation("[PrivacyController] Written default Pages/privacy.html"); + } + } + } + + private static string DefaultPrivacyHtml() => """ + + + + + +Privacy Policy - Empostor Server + + + +

Privacy Policy

+

Last updated: 2026-05-23

+ +

1. Data Collection

+

This is a private Among Us server. We collect the minimum data required for the game to function:

+
    +
  • Friend codes — used for player identification and game invitations.
  • +
  • Chat messages — processed in-game and may be logged for moderation purposes.
  • +
  • IP addresses — used for network connectivity and may be logged for security.
  • +
  • Gameplay data — tasks completed, kills, votes, and other in-game actions.
  • +
+ +

2. Data Usage

+

Collected data is used solely for:

+
    +
  • Providing the game service
  • +
  • Moderation and anti-cheat enforcement
  • +
  • Service improvement and debugging
  • +
+ +

3. Data Sharing

+

We do not sell or share your personal data with third parties. Data may be disclosed if required by law.

+ +

4. Data Retention

+

Game logs are retained for up to 31 days. Player statistics are retained while your account is active.

+ +

5. Contact

+

If you have questions about this privacy policy, contact the server administrator.

+ +

6. Third-Party Services

+

This server may use third-party plugins that process data. Each plugin operates under its own privacy terms.

+

In-game chat filters are applied for content moderation purposes.

+ + +"""; +} diff --git a/src/Empostor.Server/Plugins/PluginLoaderService.cs b/src/Empostor.Server/Plugins/PluginLoaderService.cs index 28cb773..233d731 100644 --- a/src/Empostor.Server/Plugins/PluginLoaderService.cs +++ b/src/Empostor.Server/Plugins/PluginLoaderService.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Empostor.Api.Languages; using Empostor.Api.Plugins; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -42,6 +43,17 @@ namespace Empostor.Server.Plugins plugin.Instance = instance; await plugin.Instance.EnableAsync(); + + // Register plugin-provided translations. + if (instance is IPluginLanguageProvider langProvider) + { + var translations = langProvider.GetTranslations(); + if (translations.Count > 0) + { + _serviceProvider.GetRequiredService() + .AddPluginTranslations(translations); + } + } } _logger.LogInformation( diff --git a/src/Empostor.sln b/src/Empostor.sln index 11e143e..1def983 100644 --- a/src/Empostor.sln +++ b/src/Empostor.sln @@ -37,6 +37,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugin.Code", "Emp EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugin.Narrator", "Empostor.Plugin.Narrator\Empostor.Plugin.Narrator.csproj", "{FE6D40CC-4BE5-4E38-9DCA-E6832709FE3F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Empostor.Plugins.MapVote", "Empostor.Plugins.MapVote\Empostor.Plugins.MapVote.csproj", "{9D67CAC6-D296-404C-9189-2FF9B86D287C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -227,6 +229,18 @@ Global {FE6D40CC-4BE5-4E38-9DCA-E6832709FE3F}.Release|x64.Build.0 = Release|Any CPU {FE6D40CC-4BE5-4E38-9DCA-E6832709FE3F}.Release|x86.ActiveCfg = Release|Any CPU {FE6D40CC-4BE5-4E38-9DCA-E6832709FE3F}.Release|x86.Build.0 = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|x64.ActiveCfg = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|x64.Build.0 = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|x86.ActiveCfg = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Debug|x86.Build.0 = Debug|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|Any CPU.Build.0 = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x64.ActiveCfg = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x64.Build.0 = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x86.ActiveCfg = Release|Any CPU + {9D67CAC6-D296-404C-9189-2FF9B86D287C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -243,6 +257,7 @@ Global {D4B6C8A2-5F3E-4C1D-9B7A-6E2F8A4C1D3F} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} {9D1FD146-E7D9-4AFF-9B8E-25E6021A69D7} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} {ADC4F301-51C0-45DB-B97A-BE3844DFD19B} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} + {9D67CAC6-D296-404C-9189-2FF9B86D287C} = {E0009DCB-32A6-45C4-A06C-7F62D4091A29} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DAF5625C-7E46-4DCC-AF2C-11D20D965454}