mirror of
https://github.com/Empostor/Empostor.git
synced 2026-10-19 15:51:39 +00:00
Translate Interface
This commit is contained in:
1 parent
77e3030dc0
commit
f94c0e052c
43 files changed
+1742
-2738
No files matched your search
+38
-8
@@ -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
|
||||
.gitattributes
|
||||
|
||||
# Docs
|
||||
docs/
|
||||
*.md
|
||||
|
||||
# Docker
|
||||
Dockerfile*
|
||||
.dockerignore
|
||||
compose.yml
|
||||
docker-compose*.yml
|
||||
|
||||
# Logs
|
||||
Log/
|
||||
*.log
|
||||
|
||||
# Temp
|
||||
tmp/
|
||||
temp/
|
||||
*.tmp
|
||||
+107
@@ -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"]
|
||||
+23
-5
@@ -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"]
|
||||
+11
-2
@@ -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
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because it is too large.
Load diff
@@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,117 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v8.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v8.0": {
|
||||
"Next.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",
|
||||
"Next.Hazel.Abstractions": "1.0.2.0"
|
||||
},
|
||||
"runtime": {
|
||||
"Next.Hazel.dll": {}
|
||||
}
|
||||
},
|
||||
"BouncyCastle.Cryptography/2.6.1": {
|
||||
"runtime": {
|
||||
"lib/net6.0/BouncyCastle.Cryptography.dll": {
|
||||
"assemblyVersion": "2.0.0.0",
|
||||
"fileVersion": "2.6.1.59591"
|
||||
}
|
||||
}
|
||||
},
|
||||
"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.ObjectPool/8.0.1": {
|
||||
"runtime": {
|
||||
"lib/net8.0/Microsoft.Extensions.ObjectPool.dll": {
|
||||
"assemblyVersion": "8.0.0.0",
|
||||
"fileVersion": "8.0.123.58008"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Serilog/4.3.1-dev-02373": {
|
||||
"runtime": {
|
||||
"lib/net8.0/Serilog.dll": {
|
||||
"assemblyVersion": "4.3.0.0",
|
||||
"fileVersion": "4.3.0.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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"Next.Hazel/1.0.2-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.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.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"
|
||||
},
|
||||
"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.Abstractions/1.0.2-dev": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
},
|
||||
"Next.Hazel.Abstractions/1.0.2.0": {
|
||||
"type": "reference",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -62,6 +62,32 @@ public sealed class LanguageService
|
||||
_logger.LogInformation("[Language] Reloaded all language files.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register translations provided by a plugin.
|
||||
/// Merges into existing language tables without overwriting existing keys.
|
||||
/// </summary>
|
||||
public void AddPluginTranslations(IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> translations)
|
||||
{
|
||||
foreach (var (langCode, table) in translations)
|
||||
{
|
||||
if (!_tables.TryGetValue(langCode, out var existing))
|
||||
{
|
||||
existing = new Dictionary<string, string>();
|
||||
_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 <number>",
|
||||
"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 <number>\nExample: #narrator limit 3",
|
||||
"narrator.usage": "Usage: #narrator <your question or statement>\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"
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Empostor.Api.Plugins;
|
||||
|
||||
/// <summary>
|
||||
/// Implement this on your plugin class to provide translations that are
|
||||
/// automatically registered with the language service on enable.
|
||||
/// </summary>
|
||||
public interface IPluginLanguageProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns translations grouped by language code.
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations();
|
||||
}
|
||||
@@ -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<NarratorPlugin> _logger;
|
||||
|
||||
@@ -21,4 +22,56 @@ public sealed class NarratorPlugin : PluginBase
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["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 <number>",
|
||||
["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 <number>\nExample: #narrator limit 3",
|
||||
["narrator.usage"] = "Usage: #narrator <your question or statement>\nExample: #narrator I'm being accused but I have a visual task, how do I prove myself?",
|
||||
},
|
||||
["zh_CN"] = new Dictionary<string, string>
|
||||
{
|
||||
["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<string, string>
|
||||
{
|
||||
["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 我被指控了但我有可視任務,怎麼證明自己?",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<FriendCodeValidatorPlugin> _logger;
|
||||
|
||||
@@ -20,5 +21,24 @@ namespace Empostor.Plugins.FriendCodeValidator
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["friendcode.invalid_reason"] = "Your friend code contains inappropriate words and cannot be used on this server.",
|
||||
},
|
||||
["zh_CN"] = new Dictionary<string, string>
|
||||
{
|
||||
["friendcode.invalid_reason"] = "您的好友代码包含不当词汇,无法在此服务器使用。",
|
||||
},
|
||||
["zh_TW"] = new Dictionary<string, string>
|
||||
{
|
||||
["friendcode.invalid_reason"] = "您的好友代碼包含不當詞彙,無法在此伺服器使用。",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>Empostor.Plugins.MapVote</AssemblyName>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Empostor.Api\Empostor.Api.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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 <map>";
|
||||
|
||||
public async ValueTask<bool> 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 <map>"));
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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<MapVotePlugin> _logger;
|
||||
|
||||
public MapVotePlugin(ILogger<MapVotePlugin> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override ValueTask EnableAsync()
|
||||
{
|
||||
_logger.LogInformation("[MapVote] Plugin enabled.");
|
||||
return default;
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["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 <map> to change your vote.",
|
||||
["mapvote.no_votes"] = "No map votes yet. Use #votemap <map> 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 <map>\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<string, string>
|
||||
{
|
||||
["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<string, string>
|
||||
{
|
||||
["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}。",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<string, MapTypes> 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<string, ConcurrentDictionary<string, MapTypes>> _votes = new();
|
||||
private readonly ConcurrentDictionary<string, bool> _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<string, MapTypes>());
|
||||
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<MapTypes, int> TallyVotes(string gameCode)
|
||||
{
|
||||
if (!_votes.TryGetValue(gameCode, out var gameVotes) || gameVotes.IsEmpty)
|
||||
return new Dictionary<MapTypes, int>();
|
||||
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
@@ -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<MapVoteConfig>(_configPath));
|
||||
services.AddSingleton<MapVoteService>();
|
||||
services.AddSingleton<MapVoteCommand>();
|
||||
services.AddSingleton<ICommand, MapVoteCommand>(sp => sp.GetRequiredService<MapVoteCommand>());
|
||||
services.AddSingleton<MapVoteEventListener>();
|
||||
services.AddSingleton<IEventListener, MapVoteEventListener>(sp => sp.GetRequiredService<MapVoteEventListener>());
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<AssemblyName>Empostor.Plugins.Message</AssemblyName>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Empostor.Api\Empostor.Api.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -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 <friendcode> <message>";
|
||||
|
||||
public async ValueTask<bool> ExecuteAsync(CommandContext ctx)
|
||||
{
|
||||
if (ctx.Args.Length < 2)
|
||||
{
|
||||
await ctx.PlayerControl.SendChatToPlayerAsync(
|
||||
T(ctx, "message.usage", "Usage: #msg <friendcode> <message>"), 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<PendingMessage> 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);
|
||||
}
|
||||
}
|
||||
@@ -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<MessagePlugin> _logger;
|
||||
|
||||
public MessagePlugin(ILogger<MessagePlugin> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override ValueTask EnableAsync()
|
||||
{
|
||||
_logger.LogInformation("[Message] Plugin enabled.");
|
||||
return default;
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["message.usage"] = "Usage: #msg <friendcode> <message>\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<string, string>
|
||||
{
|
||||
["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<string, string>
|
||||
{
|
||||
["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}",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<MessageConfig>(_configPath));
|
||||
services.AddSingleton<MessageStore>();
|
||||
services.AddSingleton<MessageCommand>();
|
||||
services.AddSingleton<ICommand, MessageCommand>(sp => sp.GetRequiredService<MessageCommand>());
|
||||
services.AddSingleton<MessageEventListener>();
|
||||
services.AddSingleton<IEventListener, MessageEventListener>(sp => sp.GetRequiredService<MessageEventListener>());
|
||||
}
|
||||
}
|
||||
@@ -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<string, List<PendingMessage>> _messages = new(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly ILogger<MessageStore> _logger;
|
||||
private readonly string _filePath;
|
||||
private readonly object _saveLock = new();
|
||||
|
||||
public MessageStore(ILogger<MessageStore> 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<PendingMessage>());
|
||||
lock (list)
|
||||
{
|
||||
list.Add(msg);
|
||||
}
|
||||
Save();
|
||||
}
|
||||
|
||||
public IReadOnlyList<PendingMessage> TakeAll(string targetFc)
|
||||
{
|
||||
if (!_messages.TryGetValue(targetFc, out var list) || list.Count == 0)
|
||||
return Array.Empty<PendingMessage>();
|
||||
|
||||
List<PendingMessage> 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<Dictionary<string, List<PendingMessage>>>(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<string, List<PendingMessage>>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var (fc, list) in _messages)
|
||||
{
|
||||
List<PendingMessage> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<PlayerChannelPlugin> _logger;
|
||||
|
||||
@@ -21,4 +22,29 @@ public sealed class PlayerChannelPlugin : PluginBase
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["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<string, string>
|
||||
{
|
||||
["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代码",
|
||||
["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何频道中",
|
||||
["playerchannel.message_format"] = "[{0}] {1}",
|
||||
},
|
||||
["zh_TW"] = new Dictionary<string, string>
|
||||
{
|
||||
["playerchannel.unknown_friendcode"] = "[Refuse Channel] 未知的好友代碼",
|
||||
["playerchannel.not_in_channel"] = "[Refuse Channel] 不在任何頻道中",
|
||||
["playerchannel.message_format"] = "[{0}] {1}",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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 <your QQ number>\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;
|
||||
}
|
||||
}
|
||||
@@ -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<QqVerifyPlugin> _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<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["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 <your QQ number>\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<string, string>
|
||||
{
|
||||
["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<string, string>
|
||||
{
|
||||
["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分鐘內有效。",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<TitlesPlugin> _logger;
|
||||
|
||||
@@ -18,4 +19,23 @@ public sealed class TitlesPlugin : PluginBase
|
||||
}
|
||||
|
||||
public override ValueTask DisableAsync() => default;
|
||||
|
||||
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, string>> GetTranslations()
|
||||
{
|
||||
return new Dictionary<string, IReadOnlyDictionary<string, string>>
|
||||
{
|
||||
["en"] = new Dictionary<string, string>
|
||||
{
|
||||
["titles.display_format"] = "[{0}] {1}",
|
||||
},
|
||||
["zh_CN"] = new Dictionary<string, string>
|
||||
{
|
||||
["titles.display_format"] = "[{0}] {1}",
|
||||
},
|
||||
["zh_TW"] = new Dictionary<string, string>
|
||||
{
|
||||
["titles.display_format"] = "[{0}] {1}",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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<MonitorGame> 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; } = "";
|
||||
}
|
||||
@@ -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<PrivacyController> _logger;
|
||||
|
||||
public PrivacyController(ILogger<PrivacyController> 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<IActionResult> 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() => """
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Privacy Policy - Empostor Server</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; max-width: 800px; margin: 0 auto; padding: 2rem; line-height: 1.6; color: #333; }
|
||||
h1 { border-bottom: 2px solid #eee; padding-bottom: 0.5rem; }
|
||||
h2 { margin-top: 2rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Privacy Policy</h1>
|
||||
<p>Last updated: 2026-05-23</p>
|
||||
|
||||
<h2>1. Data Collection</h2>
|
||||
<p>This is a private Among Us server. We collect the minimum data required for the game to function:</p>
|
||||
<ul>
|
||||
<li><strong>Friend codes</strong> — used for player identification and game invitations.</li>
|
||||
<li><strong>Chat messages</strong> — processed in-game and may be logged for moderation purposes.</li>
|
||||
<li><strong>IP addresses</strong> — used for network connectivity and may be logged for security.</li>
|
||||
<li><strong>Gameplay data</strong> — tasks completed, kills, votes, and other in-game actions.</li>
|
||||
</ul>
|
||||
|
||||
<h2>2. Data Usage</h2>
|
||||
<p>Collected data is used solely for:</p>
|
||||
<ul>
|
||||
<li>Providing the game service</li>
|
||||
<li>Moderation and anti-cheat enforcement</li>
|
||||
<li>Service improvement and debugging</li>
|
||||
</ul>
|
||||
|
||||
<h2>3. Data Sharing</h2>
|
||||
<p>We do not sell or share your personal data with third parties. Data may be disclosed if required by law.</p>
|
||||
|
||||
<h2>4. Data Retention</h2>
|
||||
<p>Game logs are retained for up to 31 days. Player statistics are retained while your account is active.</p>
|
||||
|
||||
<h2>5. Contact</h2>
|
||||
<p>If you have questions about this privacy policy, contact the server administrator.</p>
|
||||
|
||||
<h2>6. Third-Party Services</h2>
|
||||
<p>This server may use third-party plugins that process data. Each plugin operates under its own privacy terms.</p>
|
||||
<p>In-game chat filters are applied for content moderation purposes.</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
}
|
||||
@@ -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<LanguageService>()
|
||||
.AddPluginTranslations(translations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_logger.LogInformation(
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in new issue
Block a user