├── .editorconfig ├── .gitattributes ├── .github └── workflows │ └── action.yml ├── .gitignore ├── BindingGenerator ├── BindingGenerator.csproj ├── CoreLibrary.cs └── Program.cs ├── Core.Test ├── Core.Test.csproj ├── Program.cs └── Properties │ └── launchSettings.json ├── Core ├── Bootstrap.cs ├── Bridges │ ├── ConCommand.cs │ ├── ConVar.cs │ ├── Event.cs │ ├── SourceSharp.Core.Bridges-symbols.cpp │ └── SourceSharp.cs ├── Configurations │ └── CoreConfig.cs ├── Core.csproj ├── Dnne.Attributes.cs ├── Interfaces │ ├── ICommandListener.cs │ ├── IConVarManager.cs │ ├── IGameEventListener.cs │ ├── IListenerBase.cs │ ├── IModuleBase.cs │ ├── IPlayerListener.cs │ ├── IPlayerManagerBase.cs │ ├── IPluginManager.cs │ ├── IShareSystemBase.cs │ └── ISourceSharpBase.cs ├── Invoker.cs ├── Models │ ├── CAdmin.cs │ ├── CConVar.cs │ ├── CGameEvent.cs │ ├── CGamePlayer.cs │ ├── CHookCallback.cs │ ├── CKeyHook.cs │ └── CPlugin.cs ├── Modules │ ├── AdminManager.cs │ ├── CommandListener.cs │ ├── ConVarManager.cs │ ├── GameEventListener.cs │ ├── PlayerListener.cs │ └── PlayerManager.cs ├── PluginManager.cs ├── ShareSystem.cs ├── SourceSharp.cs ├── Utils │ └── Reflection.cs └── core.json ├── LICENSE ├── PluginExample ├── Example.cs └── PluginExample.csproj ├── README.md ├── Runtime.sln └── Sdk ├── Attributes ├── ConVarAttribute.cs ├── ConVarChangedAttribute.cs ├── ConsoleCommandAttribute.cs ├── GameEventAttribute.cs ├── GameFrameAttribute.cs ├── HookBaseAttribute.cs ├── PlayerListenerAttribute.cs └── PluginAttribute.cs ├── Enums ├── AdminFlags.cs ├── ConVarFlags.cs ├── GameEngineVersion.cs ├── GameEventHookType.cs ├── PathType.cs ├── PlayerListenerType.cs └── PluginStatus.cs ├── Interfaces ├── IAdminManager.cs ├── IGameEngine.cs ├── IPlayerManager.cs ├── IPlugin.cs ├── IRuntime.cs ├── IShareSystem.cs └── ISourceSharp.cs ├── Models ├── AdminUser.cs ├── ConVar.cs ├── ConsoleCommand.cs ├── GameEntity.cs ├── GameEvent.cs └── GamePlayer.cs ├── Sdk.csproj ├── SharedDefines.cs └── Structs ├── ActionResponse.cs └── ConVarBounds.cs /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | indent_style = space 3 | end_of_line = lf 4 | trim_trailing_whitespace = true 5 | charset = "utf-8" 6 | 7 | [*.cs] 8 | csharp_style_namespace_declarations = file_scoped:error 9 | csharp_using_directive_placement = outside_namespace:error 10 | dotnet_diagnostic.cs4014.severity = error 11 | indent_size = 4 12 | 13 | # Microsoft .NET properties 14 | csharp_new_line_before_members_in_object_initializers = true 15 | csharp_new_line_between_query_expression_clauses = true 16 | 17 | # ReSharper properties 18 | resharper_csharp_wrap_arguments_style = chop_if_long 19 | resharper_csharp_wrap_before_binary_opsign = true 20 | resharper_csharp_wrap_extends_list_style = chop_if_long 21 | resharper_csharp_wrap_multiple_declaration_style = chop_always 22 | resharper_csharp_wrap_parameters_style = chop_if_long 23 | resharper_keep_existing_embedded_arrangement = false 24 | resharper_keep_existing_expr_member_arrangement = false 25 | resharper_keep_existing_switch_expression_arrangement = false 26 | resharper_nested_ternary_style = expanded 27 | resharper_new_line_before_while = true 28 | resharper_place_accessorholder_attribute_on_same_line = false 29 | resharper_place_field_attribute_on_same_line = false 30 | resharper_place_simple_embedded_statement_on_same_line = false 31 | resharper_space_within_single_line_array_initializer_braces = true 32 | resharper_wrap_array_initializer_style = chop_if_long 33 | resharper_wrap_chained_binary_expressions = chop_if_long 34 | resharper_wrap_chained_binary_patterns = chop_if_long 35 | resharper_wrap_chained_method_calls = chop_if_long 36 | resharper_wrap_linq_expressions = chop_always 37 | resharper_wrap_list_pattern = chop_if_long 38 | 39 | [*.json] 40 | indent_size = 2 41 | 42 | [*.{xml,config}] 43 | indent_size = 2 44 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * eol=lf -------------------------------------------------------------------------------- /.github/workflows/action.yml: -------------------------------------------------------------------------------- 1 | name: Master - Push 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | name: Build on ${{ matrix.os_short }} 9 | runs-on: ${{ matrix.os }} 10 | 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: 15 | - ubuntu-20.04 16 | - windows-latest 17 | include: 18 | - os: ubuntu-20.04 19 | os_short: linux 20 | target: linux-x64 21 | - os: windows-latest 22 | os_short: windows 23 | target: win-x64 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | with: 29 | fetch-depth: 9999 30 | 31 | - name: Setup 32 | uses: actions/setup-dotnet@v3 33 | with: 34 | dotnet-version: 7.0.x 35 | 36 | - name: Nuget 37 | run: dotnet restore 38 | 39 | - name: Build 40 | shell: bash 41 | run: | 42 | COMMIT_VERSION=$(git rev-list --count HEAD) 43 | dotnet build PluginExample/PluginExample.csproj -f net7.0 -r ${{ matrix.target }} --no-self-contained -c Release 44 | dotnet publish Core/Core.csproj -f net7.0 -r ${{ matrix.target }} --no-self-contained -c Release --output ./.deploy -p:VersionSuffix=$COMMIT_VERSION 45 | 46 | - name: Publish 47 | uses: actions/upload-artifact@v3 48 | if: github.ref == 'refs/heads/master' && github.event_name == 'push' 49 | with: 50 | name: Build-${{ matrix.os_short }} 51 | path: .deploy -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | .idea 400 | 401 | # VS Code 402 | .vscode 403 | .VSCodeCounter 404 | 405 | # Build 406 | .deploy -------------------------------------------------------------------------------- /BindingGenerator/BindingGenerator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | SourceSharp.BindingGenerator 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /BindingGenerator/CoreLibrary.cs: -------------------------------------------------------------------------------- 1 | using CppSharp; 2 | using CppSharp.AST; 3 | using CppSharp.Generators; 4 | 5 | namespace SourceSharp.BindingGenerator; 6 | 7 | public class CoreLibrary : ILibrary 8 | { 9 | private readonly string _modeuleName; 10 | 11 | public CoreLibrary(string name) : base() 12 | { 13 | _modeuleName = name; 14 | } 15 | 16 | public void Preprocess(Driver driver, ASTContext ctx) 17 | { 18 | } 19 | 20 | public void Postprocess(Driver driver, ASTContext ctx) 21 | { 22 | } 23 | 24 | public void Setup(Driver driver) 25 | { 26 | var projectFolder = Environment.GetEnvironmentVariable("SOURCESHARP")!; 27 | 28 | var options = driver.Options; 29 | options.Verbose = true; 30 | options.GeneratorKind = GeneratorKind.CSharp; 31 | options.OutputDir = Path.Combine(projectFolder, "runtime", "Core", "Bridges"); 32 | 33 | var m = options.AddModule(_modeuleName); 34 | m.IncludeDirs.Add(Path.Combine(projectFolder, "engine", "include", "modules")); 35 | m.Headers.Add(_modeuleName + ".h"); 36 | m.SharedLibraryName = "sourcesharp"; 37 | m.OutputNamespace = "SourceSharp.Core.Bridges"; 38 | 39 | // var module = options.AddModule("SourceSharp.Core.ConCommand"); 40 | // module.IncludeDirs.Add(Path.Combine(projectFolder, "engine", "include")); 41 | // module.IncludeDirs.Add(Path.Combine(projectFolder, "engine", "include", "modules")); 42 | // module.Headers.Add("Core.h"); 43 | // module.SharedLibraryName = "sourcesharp"; 44 | // module.LibraryDirs.Add(@"C:\Users\Bone\CLionProjects\SourceSharp\build\windows\x86\release"); 45 | // module.Libraries.Add("sourcesharp.dll"); 46 | } 47 | 48 | public void SetupPasses(Driver driver) 49 | { 50 | driver.Context.TranslationUnitPasses.AddPass(new CppSharp.Passes.FunctionToInstanceMethodPass()); 51 | driver.Context.TranslationUnitPasses.AddPass(new CppSharp.Passes.FunctionToStaticMethodPass()); 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /BindingGenerator/Program.cs: -------------------------------------------------------------------------------- 1 | using CppSharp; 2 | using SourceSharp.BindingGenerator; 3 | 4 | foreach (var module in new string [] { 5 | "SourceSharp", 6 | "ConCommand", 7 | "Event", 8 | "ConVar", 9 | }) 10 | { 11 | ConsoleDriver.Run(new CoreLibrary(module)); 12 | } 13 | Console.WriteLine("done"); 14 | Console.ReadKey(true); 15 | -------------------------------------------------------------------------------- /Core.Test/Core.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net7.0 6 | disable 7 | 11 8 | enable 9 | SourceSharp.Core.Test 10 | SourceSharp.Core.Test 11 | 1.0 12 | 1 13 | $(VersionPrefix).$(VersionSuffix) 14 | https://github.com/SourceSharp 15 | ©2023 Kyle, Bone 16 | https://github.com/SourceSharp/runtime 17 | true 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Core.Test/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace SourceSharp.Core.Test; 5 | 6 | internal static class Program 7 | { 8 | static async Task Main() 9 | { 10 | Bootstrap.InitializeTest(); 11 | await Task.Delay(TimeSpan.FromDays(1)); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Core.Test/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "Core.Test": { 4 | "commandName": "Project", 5 | "commandLineArgs": "-testCommannLine 123" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /Core/Bootstrap.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using SourceSharp.Core.Configurations; 4 | using SourceSharp.Core.Interfaces; 5 | using SourceSharp.Core.Modules; 6 | using SourceSharp.Core.Utils; 7 | using SourceSharp.Sdk.Interfaces; 8 | using System; 9 | using System.Diagnostics; 10 | using System.IO; 11 | using System.Reflection; 12 | using System.Runtime.InteropServices; 13 | using System.Threading.Tasks; 14 | 15 | namespace SourceSharp.Core; 16 | 17 | public static class Bootstrap 18 | { 19 | private static bool _isShutdown; 20 | private static Task? _signalTask; 21 | 22 | [UnmanagedCallersOnly] 23 | public static int InitializeSourceSharp() => Initialize(); 24 | 25 | [UnmanagedCallersOnly] 26 | public static void ShutdownSourceSharp() 27 | { 28 | _isShutdown = true; 29 | _signalTask?.Wait(); 30 | } 31 | 32 | public static int InitializeTest() => Initialize(); 33 | 34 | private static int Initialize() 35 | { 36 | _isShutdown = false; 37 | 38 | try 39 | { 40 | var root = Path.Combine(Path.GetDirectoryName( 41 | Assembly.GetExecutingAssembly().Location)!, 42 | Bridges.SourceSharp.GetGamePath(), 43 | "addons", 44 | "sourcesharp"); 45 | 46 | // config 47 | var path = Path.Combine(root, "configs", "core.json"); 48 | 49 | var services = new ServiceCollection(); 50 | var configuration = new ConfigurationBuilder().AddJsonFile(path).Build(); 51 | 52 | ConfigureServices(services, configuration); 53 | 54 | var serviceProvider = services.BuildServiceProvider(options: new() 55 | { 56 | ValidateOnBuild = true, 57 | ValidateScopes = true 58 | }); 59 | 60 | Debug.Print("MaxClients is " + Bridges.SourceSharp.GetMaxClients()); 61 | Debug.Print("MaxHumanPlayers is " + Bridges.SourceSharp.GetMaxHumanPlayers()); 62 | 63 | Boot(serviceProvider); 64 | 65 | return 0; 66 | } 67 | catch (Exception ex) 68 | { 69 | var color = Console.ForegroundColor; 70 | Console.ForegroundColor = ConsoleColor.Cyan; 71 | Console.WriteLine($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} [SourceSharp] Failed to init SourceSharp."); 72 | Console.ForegroundColor = color; 73 | Console.WriteLine(ex.ToString()); 74 | Console.WriteLine(Environment.NewLine); 75 | return 1; 76 | } 77 | } 78 | 79 | private static void ConfigureServices(IServiceCollection services, IConfiguration configuration) 80 | { 81 | services.AddSingleton(configuration.GetSection("Core").Get() ?? 82 | throw new InvalidDataException("Core config is missing!")); 83 | 84 | services.AddSingleton(); 85 | services.AddSingleton(); 86 | 87 | services.AddSingleton(); 88 | services.AddSingleton(); 89 | services.AddSingleton(); 90 | //services.AddSingleton(); 91 | services.AddSingleton(); 92 | services.AddSingleton(); 93 | 94 | services.AddSingleton(); 95 | } 96 | 97 | private static void Boot(IServiceProvider services) 98 | { 99 | // Init IModuleBase 100 | foreach (var module in services.GetAllServices()) 101 | { 102 | module.Initialize(); 103 | } 104 | 105 | // Plugin Manager should be the LAST! 106 | services.GetRequiredService().Initialize(services); 107 | 108 | // export caller invoker 109 | Invoker.Initialize(services); 110 | 111 | _signalTask = Task.Run(async () => await SignalThread(services)); 112 | } 113 | 114 | private static async Task SignalThread(IServiceProvider services) 115 | { 116 | var pluginManager = services.GetRequiredService(); 117 | 118 | while (true) 119 | { 120 | await Task.Delay(TimeSpan.FromMicroseconds(1)); 121 | 122 | if (_isShutdown) 123 | { 124 | // Shutdown !!! 125 | foreach (var module in services.GetAllServices()) 126 | { 127 | module.Shutdown(); 128 | } 129 | services.GetRequiredService().Shutdown(); 130 | return; 131 | } 132 | 133 | pluginManager.Signal(); 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /Core/Bridges/ConCommand.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // This is autogenerated code by CppSharp. 4 | // Do not edit this file or all your changes will be lost after re-generation. 5 | // 6 | // ---------------------------------------------------------------------------- 7 | using System; 8 | using System.Runtime.InteropServices; 9 | using System.Security; 10 | using __CallingConvention = global::System.Runtime.InteropServices.CallingConvention; 11 | using __IntPtr = global::System.IntPtr; 12 | 13 | #pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required 14 | 15 | namespace SourceSharp.Core.Bridges 16 | { 17 | public unsafe partial class ConCommand 18 | { 19 | public partial struct __Internal 20 | { 21 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "RegServerCommand", CallingConvention = __CallingConvention.Cdecl)] 22 | internal static extern void RegServerCommand([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string command); 23 | 24 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "RegClientCommand", CallingConvention = __CallingConvention.Cdecl)] 25 | internal static extern void RegClientCommand([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string command); 26 | } 27 | 28 | public static void RegServerCommand(string command) 29 | { 30 | __Internal.RegServerCommand(command); 31 | } 32 | 33 | public static void RegClientCommand(string command) 34 | { 35 | __Internal.RegClientCommand(command); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Core/Bridges/ConVar.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // This is autogenerated code by CppSharp. 4 | // Do not edit this file or all your changes will be lost after re-generation. 5 | // 6 | // ---------------------------------------------------------------------------- 7 | using System; 8 | using System.Runtime.InteropServices; 9 | using System.Security; 10 | using __CallingConvention = global::System.Runtime.InteropServices.CallingConvention; 11 | using __IntPtr = global::System.IntPtr; 12 | 13 | #pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required 14 | 15 | namespace SourceSharp.Core.Bridges 16 | { 17 | public unsafe partial class SSConVar 18 | { 19 | public partial struct __Internal 20 | { 21 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarAddFlags", CallingConvention = __CallingConvention.Cdecl)] 22 | internal static extern void AddFlags(__IntPtr __instance, int nFlags); 23 | 24 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarSetBound", CallingConvention = __CallingConvention.Cdecl)] 25 | internal static extern void SetBound(__IntPtr __instance, bool bHasMin, float flMin, bool bHasMax, float flMax); 26 | 27 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarRevert", CallingConvention = __CallingConvention.Cdecl)] 28 | internal static extern void Revert(__IntPtr __instance); 29 | 30 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarReplicateToPlayers", CallingConvention = __CallingConvention.Cdecl)] 31 | [return: MarshalAs(UnmanagedType.I1)] 32 | internal static extern bool ReplicateToPlayers(__IntPtr __instance, int[] pPlayers, int nPlayers); 33 | 34 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetName", CallingConvention = __CallingConvention.Cdecl)] 35 | internal static extern __IntPtr GetName(__IntPtr __instance); 36 | 37 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetDefault", CallingConvention = __CallingConvention.Cdecl)] 38 | internal static extern __IntPtr GetDefault(__IntPtr __instance); 39 | 40 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetDescription", CallingConvention = __CallingConvention.Cdecl)] 41 | internal static extern __IntPtr GetDescription(__IntPtr __instance); 42 | 43 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetFlags", CallingConvention = __CallingConvention.Cdecl)] 44 | internal static extern int GetFlags(__IntPtr __instance); 45 | 46 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetInt", CallingConvention = __CallingConvention.Cdecl)] 47 | internal static extern int GetInt(__IntPtr __instance); 48 | 49 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarSetInt", CallingConvention = __CallingConvention.Cdecl)] 50 | internal static extern void SetInt(__IntPtr __instance, int val); 51 | 52 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetBool", CallingConvention = __CallingConvention.Cdecl)] 53 | [return: MarshalAs(UnmanagedType.I1)] 54 | internal static extern bool GetBool(__IntPtr __instance); 55 | 56 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarSetBool", CallingConvention = __CallingConvention.Cdecl)] 57 | internal static extern void SetBool(__IntPtr __instance, bool val); 58 | 59 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetFloat", CallingConvention = __CallingConvention.Cdecl)] 60 | internal static extern float GetFloat(__IntPtr __instance); 61 | 62 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarSetFloat", CallingConvention = __CallingConvention.Cdecl)] 63 | internal static extern void SetFloat(__IntPtr __instance, float val); 64 | 65 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetString", CallingConvention = __CallingConvention.Cdecl)] 66 | internal static extern __IntPtr GetString(__IntPtr __instance); 67 | 68 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarSetString", CallingConvention = __CallingConvention.Cdecl)] 69 | internal static extern void SetString(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string val); 70 | 71 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetMinValue", CallingConvention = __CallingConvention.Cdecl)] 72 | internal static extern float GetMinValue(__IntPtr __instance); 73 | 74 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetMaxValue", CallingConvention = __CallingConvention.Cdecl)] 75 | internal static extern float GetMaxValue(__IntPtr __instance); 76 | 77 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetHasMin", CallingConvention = __CallingConvention.Cdecl)] 78 | [return: MarshalAs(UnmanagedType.I1)] 79 | internal static extern bool GetHasMin(__IntPtr __instance); 80 | 81 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "SSConVarGetHasMax", CallingConvention = __CallingConvention.Cdecl)] 82 | [return: MarshalAs(UnmanagedType.I1)] 83 | internal static extern bool GetHasMax(__IntPtr __instance); 84 | } 85 | 86 | public __IntPtr __Instance { get; protected set; } 87 | 88 | internal static readonly new global::System.Collections.Concurrent.ConcurrentDictionary NativeToManagedMap = 89 | new global::System.Collections.Concurrent.ConcurrentDictionary(); 90 | 91 | internal static void __RecordNativeToManagedMapping(IntPtr native, global::SourceSharp.Core.Bridges.SSConVar managed) 92 | { 93 | NativeToManagedMap[native] = managed; 94 | } 95 | 96 | internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::SourceSharp.Core.Bridges.SSConVar managed) 97 | { 98 | 99 | return NativeToManagedMap.TryGetValue(native, out managed); 100 | } 101 | 102 | protected bool __ownsNativeInstance; 103 | 104 | internal static SSConVar __CreateInstance(__IntPtr native, bool skipVTables = false) 105 | { 106 | if (native == __IntPtr.Zero) 107 | return null; 108 | return new SSConVar(native.ToPointer(), skipVTables); 109 | } 110 | 111 | internal static SSConVar __GetOrCreateInstance(__IntPtr native, bool saveInstance = false, bool skipVTables = false) 112 | { 113 | if (native == __IntPtr.Zero) 114 | return null; 115 | if (__TryGetNativeToManagedMapping(native, out var managed)) 116 | return (SSConVar)managed; 117 | var result = __CreateInstance(native, skipVTables); 118 | if (saveInstance) 119 | __RecordNativeToManagedMapping(native, result); 120 | return result; 121 | } 122 | 123 | internal static SSConVar __CreateInstance(__Internal native, bool skipVTables = false) 124 | { 125 | return new SSConVar(native, skipVTables); 126 | } 127 | 128 | private static void* __CopyValue(__Internal native) 129 | { 130 | var ret = Marshal.AllocHGlobal(sizeof(__Internal)); 131 | *(__Internal*) ret = native; 132 | return ret.ToPointer(); 133 | } 134 | 135 | private SSConVar(__Internal native, bool skipVTables = false) 136 | : this(__CopyValue(native), skipVTables) 137 | { 138 | __ownsNativeInstance = true; 139 | __RecordNativeToManagedMapping(__Instance, this); 140 | } 141 | 142 | protected SSConVar(void* native, bool skipVTables = false) 143 | { 144 | if (native == null) 145 | return; 146 | __Instance = new __IntPtr(native); 147 | } 148 | 149 | public void AddFlags(int nFlags) 150 | { 151 | __Internal.AddFlags(__Instance, nFlags); 152 | } 153 | 154 | public void SetBound(bool bHasMin, float flMin, bool bHasMax, float flMax) 155 | { 156 | __Internal.SetBound(__Instance, bHasMin, flMin, bHasMax, flMax); 157 | } 158 | 159 | public void Revert() 160 | { 161 | __Internal.Revert(__Instance); 162 | } 163 | 164 | public bool ReplicateToPlayers(int[] pPlayers, int nPlayers) 165 | { 166 | var ___ret = __Internal.ReplicateToPlayers(__Instance, pPlayers, nPlayers); 167 | return ___ret; 168 | } 169 | 170 | public string Name 171 | { 172 | get 173 | { 174 | var ___ret = __Internal.GetName(__Instance); 175 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 176 | } 177 | } 178 | 179 | public string Default 180 | { 181 | get 182 | { 183 | var ___ret = __Internal.GetDefault(__Instance); 184 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 185 | } 186 | } 187 | 188 | public string Description 189 | { 190 | get 191 | { 192 | var ___ret = __Internal.GetDescription(__Instance); 193 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 194 | } 195 | } 196 | 197 | public int Flags 198 | { 199 | get 200 | { 201 | var ___ret = __Internal.GetFlags(__Instance); 202 | return ___ret; 203 | } 204 | } 205 | 206 | public int Int 207 | { 208 | get 209 | { 210 | var ___ret = __Internal.GetInt(__Instance); 211 | return ___ret; 212 | } 213 | 214 | set 215 | { 216 | __Internal.SetInt(__Instance, value); 217 | } 218 | } 219 | 220 | public bool Bool 221 | { 222 | get 223 | { 224 | var ___ret = __Internal.GetBool(__Instance); 225 | return ___ret; 226 | } 227 | 228 | set 229 | { 230 | __Internal.SetBool(__Instance, value); 231 | } 232 | } 233 | 234 | public float Float 235 | { 236 | get 237 | { 238 | var ___ret = __Internal.GetFloat(__Instance); 239 | return ___ret; 240 | } 241 | 242 | set 243 | { 244 | __Internal.SetFloat(__Instance, value); 245 | } 246 | } 247 | 248 | public string String 249 | { 250 | get 251 | { 252 | var ___ret = __Internal.GetString(__Instance); 253 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 254 | } 255 | 256 | set 257 | { 258 | __Internal.SetString(__Instance, value); 259 | } 260 | } 261 | 262 | public float MinValue 263 | { 264 | get 265 | { 266 | var ___ret = __Internal.GetMinValue(__Instance); 267 | return ___ret; 268 | } 269 | } 270 | 271 | public float MaxValue 272 | { 273 | get 274 | { 275 | var ___ret = __Internal.GetMaxValue(__Instance); 276 | return ___ret; 277 | } 278 | } 279 | 280 | public bool HasMin 281 | { 282 | get 283 | { 284 | var ___ret = __Internal.GetHasMin(__Instance); 285 | return ___ret; 286 | } 287 | } 288 | 289 | public bool HasMax 290 | { 291 | get 292 | { 293 | var ___ret = __Internal.GetHasMax(__Instance); 294 | return ___ret; 295 | } 296 | } 297 | } 298 | 299 | public unsafe partial class ConVar 300 | { 301 | public partial struct __Internal 302 | { 303 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "CreateConVar", CallingConvention = __CallingConvention.Cdecl)] 304 | internal static extern __IntPtr CreateConVar([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pName, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pDECLValue, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pDescription, int nFlags, bool bHasMin, float flMin, bool bHasMax, float flMax); 305 | 306 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "FindConVar", CallingConvention = __CallingConvention.Cdecl)] 307 | internal static extern __IntPtr FindConVar([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pName); 308 | 309 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "RegisterConVarHook", CallingConvention = __CallingConvention.Cdecl)] 310 | [return: MarshalAs(UnmanagedType.I1)] 311 | internal static extern bool RegisterConVarHook([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pName); 312 | 313 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "TestConVar", CallingConvention = __CallingConvention.Cdecl)] 314 | internal static extern int TestConVar(__IntPtr pVar); 315 | } 316 | 317 | public static global::SourceSharp.Core.Bridges.SSConVar CreateConVar(string pName, string pDECLValue, string pDescription, int nFlags, bool bHasMin, float flMin, bool bHasMax, float flMax) 318 | { 319 | var ___ret = __Internal.CreateConVar(pName, pDECLValue, pDescription, nFlags, bHasMin, flMin, bHasMax, flMax); 320 | var __result0 = global::SourceSharp.Core.Bridges.SSConVar.__GetOrCreateInstance(___ret, false); 321 | return __result0; 322 | } 323 | 324 | public static global::SourceSharp.Core.Bridges.SSConVar FindConVar(string pName) 325 | { 326 | var ___ret = __Internal.FindConVar(pName); 327 | var __result0 = global::SourceSharp.Core.Bridges.SSConVar.__GetOrCreateInstance(___ret, false); 328 | return __result0; 329 | } 330 | 331 | public static bool RegisterConVarHook(string pName) 332 | { 333 | var ___ret = __Internal.RegisterConVarHook(pName); 334 | return ___ret; 335 | } 336 | 337 | public static int TestConVar(global::SourceSharp.Core.Bridges.SSConVar pVar) 338 | { 339 | var __arg0 = pVar is null ? __IntPtr.Zero : pVar.__Instance; 340 | var ___ret = __Internal.TestConVar(__arg0); 341 | return ___ret; 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /Core/Bridges/Event.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // This is autogenerated code by CppSharp. 4 | // Do not edit this file or all your changes will be lost after re-generation. 5 | // 6 | // ---------------------------------------------------------------------------- 7 | using System; 8 | using System.Runtime.InteropServices; 9 | using System.Security; 10 | using __CallingConvention = global::System.Runtime.InteropServices.CallingConvention; 11 | using __IntPtr = global::System.IntPtr; 12 | 13 | #pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required 14 | 15 | namespace SourceSharp.Core.Bridges 16 | { 17 | public unsafe partial class SSGameEvent : IDisposable 18 | { 19 | [StructLayout(LayoutKind.Sequential, Size = 8)] 20 | public partial struct __Internal 21 | { 22 | internal __IntPtr m_pEvent; 23 | 24 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "??0SSGameEvent@@QEAA@AEBV0@@Z", CallingConvention = __CallingConvention.Cdecl)] 25 | internal static extern __IntPtr cctor(__IntPtr __instance, __IntPtr _0); 26 | 27 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?SetInt@SSGameEvent@@QEAA_NPEBDH@Z", CallingConvention = __CallingConvention.Cdecl)] 28 | [return: MarshalAs(UnmanagedType.I1)] 29 | internal static extern bool SetInt(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey, int value); 30 | 31 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetInt@SSGameEvent@@QEAAHPEBD@Z", CallingConvention = __CallingConvention.Cdecl)] 32 | internal static extern int GetInt(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey); 33 | 34 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?SetBool@SSGameEvent@@QEAA_NPEBD_N@Z", CallingConvention = __CallingConvention.Cdecl)] 35 | [return: MarshalAs(UnmanagedType.I1)] 36 | internal static extern bool SetBool(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey, bool value); 37 | 38 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetBool@SSGameEvent@@QEAA_NPEBD@Z", CallingConvention = __CallingConvention.Cdecl)] 39 | [return: MarshalAs(UnmanagedType.I1)] 40 | internal static extern bool GetBool(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey); 41 | 42 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?SetFloat@SSGameEvent@@QEAA_NPEBDM@Z", CallingConvention = __CallingConvention.Cdecl)] 43 | [return: MarshalAs(UnmanagedType.I1)] 44 | internal static extern bool SetFloat(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey, float value); 45 | 46 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetFloat@SSGameEvent@@QEAAMPEBD@Z", CallingConvention = __CallingConvention.Cdecl)] 47 | internal static extern float GetFloat(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey); 48 | 49 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?SetString@SSGameEvent@@QEAA_NPEBD0@Z", CallingConvention = __CallingConvention.Cdecl)] 50 | [return: MarshalAs(UnmanagedType.I1)] 51 | internal static extern bool SetString(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string value); 52 | 53 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetString@SSGameEvent@@QEAAPEBDPEBD@Z", CallingConvention = __CallingConvention.Cdecl)] 54 | internal static extern __IntPtr GetString(__IntPtr __instance, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pKey); 55 | 56 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?SetBroadcast@SSGameEvent@@QEAA_N_N@Z", CallingConvention = __CallingConvention.Cdecl)] 57 | [return: MarshalAs(UnmanagedType.I1)] 58 | internal static extern bool SetBroadcast(__IntPtr __instance, bool value); 59 | 60 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?Cancel@SSGameEvent@@QEAAXXZ", CallingConvention = __CallingConvention.Cdecl)] 61 | internal static extern void Cancel(__IntPtr __instance); 62 | 63 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetName@SSGameEvent@@QEAAPEBDXZ", CallingConvention = __CallingConvention.Cdecl)] 64 | internal static extern __IntPtr GetName(__IntPtr __instance); 65 | 66 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "?GetBroadcast@SSGameEvent@@QEAA_NXZ", CallingConvention = __CallingConvention.Cdecl)] 67 | [return: MarshalAs(UnmanagedType.I1)] 68 | internal static extern bool GetBroadcast(__IntPtr __instance); 69 | } 70 | 71 | public __IntPtr __Instance { get; protected set; } 72 | 73 | internal static readonly new global::System.Collections.Concurrent.ConcurrentDictionary NativeToManagedMap = 74 | new global::System.Collections.Concurrent.ConcurrentDictionary(); 75 | 76 | internal static void __RecordNativeToManagedMapping(IntPtr native, global::SourceSharp.Core.Bridges.SSGameEvent managed) 77 | { 78 | NativeToManagedMap[native] = managed; 79 | } 80 | 81 | internal static bool __TryGetNativeToManagedMapping(IntPtr native, out global::SourceSharp.Core.Bridges.SSGameEvent managed) 82 | { 83 | 84 | return NativeToManagedMap.TryGetValue(native, out managed); 85 | } 86 | 87 | protected bool __ownsNativeInstance; 88 | 89 | internal static SSGameEvent __CreateInstance(__IntPtr native, bool skipVTables = false) 90 | { 91 | if (native == __IntPtr.Zero) 92 | return null; 93 | return new SSGameEvent(native.ToPointer(), skipVTables); 94 | } 95 | 96 | internal static SSGameEvent __GetOrCreateInstance(__IntPtr native, bool saveInstance = false, bool skipVTables = false) 97 | { 98 | if (native == __IntPtr.Zero) 99 | return null; 100 | if (__TryGetNativeToManagedMapping(native, out var managed)) 101 | return (SSGameEvent)managed; 102 | var result = __CreateInstance(native, skipVTables); 103 | if (saveInstance) 104 | __RecordNativeToManagedMapping(native, result); 105 | return result; 106 | } 107 | 108 | internal static SSGameEvent __CreateInstance(__Internal native, bool skipVTables = false) 109 | { 110 | return new SSGameEvent(native, skipVTables); 111 | } 112 | 113 | private static void* __CopyValue(__Internal native) 114 | { 115 | var ret = Marshal.AllocHGlobal(sizeof(__Internal)); 116 | *(__Internal*) ret = native; 117 | return ret.ToPointer(); 118 | } 119 | 120 | private SSGameEvent(__Internal native, bool skipVTables = false) 121 | : this(__CopyValue(native), skipVTables) 122 | { 123 | __ownsNativeInstance = true; 124 | __RecordNativeToManagedMapping(__Instance, this); 125 | } 126 | 127 | protected SSGameEvent(void* native, bool skipVTables = false) 128 | { 129 | if (native == null) 130 | return; 131 | __Instance = new __IntPtr(native); 132 | } 133 | 134 | public SSGameEvent() 135 | { 136 | __Instance = Marshal.AllocHGlobal(sizeof(global::SourceSharp.Core.Bridges.SSGameEvent.__Internal)); 137 | __ownsNativeInstance = true; 138 | __RecordNativeToManagedMapping(__Instance, this); 139 | } 140 | 141 | public SSGameEvent(global::SourceSharp.Core.Bridges.SSGameEvent _0) 142 | { 143 | __Instance = Marshal.AllocHGlobal(sizeof(global::SourceSharp.Core.Bridges.SSGameEvent.__Internal)); 144 | __ownsNativeInstance = true; 145 | __RecordNativeToManagedMapping(__Instance, this); 146 | *((global::SourceSharp.Core.Bridges.SSGameEvent.__Internal*) __Instance) = *((global::SourceSharp.Core.Bridges.SSGameEvent.__Internal*) _0.__Instance); 147 | } 148 | 149 | public void Dispose() 150 | { 151 | Dispose(disposing: true, callNativeDtor : __ownsNativeInstance ); 152 | } 153 | 154 | partial void DisposePartial(bool disposing); 155 | 156 | internal protected virtual void Dispose(bool disposing, bool callNativeDtor ) 157 | { 158 | if (__Instance == IntPtr.Zero) 159 | return; 160 | NativeToManagedMap.TryRemove(__Instance, out _); 161 | DisposePartial(disposing); 162 | if (__ownsNativeInstance) 163 | Marshal.FreeHGlobal(__Instance); 164 | __Instance = IntPtr.Zero; 165 | } 166 | 167 | public bool SetInt(string pKey, int value) 168 | { 169 | var ___ret = __Internal.SetInt(__Instance, pKey, value); 170 | return ___ret; 171 | } 172 | 173 | public int GetInt(string pKey) 174 | { 175 | var ___ret = __Internal.GetInt(__Instance, pKey); 176 | return ___ret; 177 | } 178 | 179 | public bool SetBool(string pKey, bool value) 180 | { 181 | var ___ret = __Internal.SetBool(__Instance, pKey, value); 182 | return ___ret; 183 | } 184 | 185 | public bool GetBool(string pKey) 186 | { 187 | var ___ret = __Internal.GetBool(__Instance, pKey); 188 | return ___ret; 189 | } 190 | 191 | public bool SetFloat(string pKey, float value) 192 | { 193 | var ___ret = __Internal.SetFloat(__Instance, pKey, value); 194 | return ___ret; 195 | } 196 | 197 | public float GetFloat(string pKey) 198 | { 199 | var ___ret = __Internal.GetFloat(__Instance, pKey); 200 | return ___ret; 201 | } 202 | 203 | public bool SetString(string pKey, string value) 204 | { 205 | var ___ret = __Internal.SetString(__Instance, pKey, value); 206 | return ___ret; 207 | } 208 | 209 | public string GetString(string pKey) 210 | { 211 | var ___ret = __Internal.GetString(__Instance, pKey); 212 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 213 | } 214 | 215 | public bool SetBroadcast(bool value) 216 | { 217 | var ___ret = __Internal.SetBroadcast(__Instance, value); 218 | return ___ret; 219 | } 220 | 221 | public void Cancel() 222 | { 223 | __Internal.Cancel(__Instance); 224 | } 225 | 226 | public string Name 227 | { 228 | get 229 | { 230 | var ___ret = __Internal.GetName(__Instance); 231 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 232 | } 233 | } 234 | 235 | public bool Broadcast 236 | { 237 | get 238 | { 239 | var ___ret = __Internal.GetBroadcast(__Instance); 240 | return ___ret; 241 | } 242 | 243 | set 244 | { 245 | __Internal.SetBroadcast(__Instance, value); 246 | } 247 | } 248 | } 249 | 250 | public unsafe partial class Event 251 | { 252 | public partial struct __Internal 253 | { 254 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "RegGameEventHook", CallingConvention = __CallingConvention.Cdecl)] 255 | [return: MarshalAs(UnmanagedType.I1)] 256 | internal static extern bool RegGameEventHook([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pName); 257 | 258 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "CreateGameEvent", CallingConvention = __CallingConvention.Cdecl)] 259 | internal static extern __IntPtr CreateGameEvent([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pName, bool bBroadcast); 260 | } 261 | 262 | public static bool RegGameEventHook(string pName) 263 | { 264 | var ___ret = __Internal.RegGameEventHook(pName); 265 | return ___ret; 266 | } 267 | 268 | public static global::SourceSharp.Core.Bridges.SSGameEvent CreateGameEvent(string pName, bool bBroadcast) 269 | { 270 | var ___ret = __Internal.CreateGameEvent(pName, bBroadcast); 271 | var __result0 = global::SourceSharp.Core.Bridges.SSGameEvent.__GetOrCreateInstance(___ret, false); 272 | return __result0; 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Core/Bridges/SourceSharp.Core.Bridges-symbols.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class SSGameEvent& (SSGameEvent::*_0)(class SSGameEvent&&) = &SSGameEvent::operator=; 5 | -------------------------------------------------------------------------------- /Core/Bridges/SourceSharp.cs: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // 3 | // This is autogenerated code by CppSharp. 4 | // Do not edit this file or all your changes will be lost after re-generation. 5 | // 6 | // ---------------------------------------------------------------------------- 7 | using System; 8 | using System.Runtime.InteropServices; 9 | using System.Security; 10 | using __CallingConvention = global::System.Runtime.InteropServices.CallingConvention; 11 | using __IntPtr = global::System.IntPtr; 12 | 13 | #pragma warning disable CS0109 // Member does not hide an inherited member; new keyword is not required 14 | 15 | namespace SourceSharp.Core.Bridges 16 | { 17 | public unsafe partial class SourceSharp 18 | { 19 | public partial struct __Internal 20 | { 21 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "GetGamePath", CallingConvention = __CallingConvention.Cdecl)] 22 | internal static extern __IntPtr GetGamePath(); 23 | 24 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "LogError", CallingConvention = __CallingConvention.Cdecl)] 25 | internal static extern void LogError([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pMessage); 26 | 27 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "LogMessage", CallingConvention = __CallingConvention.Cdecl)] 28 | internal static extern void LogMessage([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pMessage); 29 | 30 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "GetMaxClients", CallingConvention = __CallingConvention.Cdecl)] 31 | internal static extern int GetMaxClients(); 32 | 33 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "GetMaxHumanPlayers", CallingConvention = __CallingConvention.Cdecl)] 34 | internal static extern int GetMaxHumanPlayers(); 35 | 36 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "GetEngineVersion", CallingConvention = __CallingConvention.Cdecl)] 37 | internal static extern int GetEngineVersion(); 38 | 39 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "ExecuteServerCommand", CallingConvention = __CallingConvention.Cdecl)] 40 | internal static extern void ExecuteServerCommand([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pCommand); 41 | 42 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "InsertServerCommand", CallingConvention = __CallingConvention.Cdecl)] 43 | internal static extern void InsertServerCommand([MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(CppSharp.Runtime.UTF8Marshaller))] string pCommand); 44 | 45 | [SuppressUnmanagedCodeSecurity, DllImport("sourcesharp", EntryPoint = "ServerExecute", CallingConvention = __CallingConvention.Cdecl)] 46 | internal static extern void ServerExecute(); 47 | } 48 | 49 | public static string GetGamePath() 50 | { 51 | var ___ret = __Internal.GetGamePath(); 52 | return CppSharp.Runtime.MarshalUtil.GetString(global::System.Text.Encoding.UTF8, ___ret); 53 | } 54 | 55 | public static void LogError(string pMessage) 56 | { 57 | __Internal.LogError(pMessage); 58 | } 59 | 60 | public static void LogMessage(string pMessage) 61 | { 62 | __Internal.LogMessage(pMessage); 63 | } 64 | 65 | public static int GetMaxClients() 66 | { 67 | var ___ret = __Internal.GetMaxClients(); 68 | return ___ret; 69 | } 70 | 71 | public static int GetMaxHumanPlayers() 72 | { 73 | var ___ret = __Internal.GetMaxHumanPlayers(); 74 | return ___ret; 75 | } 76 | 77 | public static int GetEngineVersion() 78 | { 79 | var ___ret = __Internal.GetEngineVersion(); 80 | return ___ret; 81 | } 82 | 83 | public static void ExecuteServerCommand(string pCommand) 84 | { 85 | __Internal.ExecuteServerCommand(pCommand); 86 | } 87 | 88 | public static void InsertServerCommand(string pCommand) 89 | { 90 | __Internal.InsertServerCommand(pCommand); 91 | } 92 | 93 | public static void ServerExecute() 94 | { 95 | __Internal.ServerExecute(); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Core/Configurations/CoreConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace SourceSharp.Core.Configurations; 4 | internal sealed class CoreConfig 5 | { 6 | [JsonRequired] 7 | public bool AllowUnsafe { get; set; } 8 | } 9 | -------------------------------------------------------------------------------- /Core/Core.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net7.0 4 | disable 5 | 11 6 | enable 7 | SourceSharp.Core 8 | SourceSharp.Core 9 | 1.0 10 | 1 11 | $(VersionPrefix).$(VersionSuffix) 12 | https://github.com/SourceSharp 13 | ©2023 Kyle, Bone 14 | https://github.com/SourceSharp/runtime 15 | SourceSharp.Runtime 16 | true 17 | true 18 | 19 | 20 | True 21 | x86 22 | win-x86 23 | 24 | 25 | True 26 | x64 27 | win-x64 28 | 29 | 30 | True 31 | x86 32 | win-x86 33 | 34 | 35 | True 36 | x64 37 | win-x64 38 | 39 | 40 | 41 | 42 | 43 | 44 | Always 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Core/Dnne.Attributes.cs: -------------------------------------------------------------------------------- 1 | namespace DNNE; 2 | 3 | #nullable disable 4 | #pragma warning disable CA1018 // 用 AttributeUsageAttribute 标记属性 5 | #pragma warning disable IDE0060 6 | 7 | internal class ExportAttribute : System.Attribute 8 | { 9 | public ExportAttribute() { } 10 | public string EntryPoint { get; set; } 11 | } 12 | 13 | 14 | internal class C99TypeAttribute : System.Attribute 15 | { 16 | public C99TypeAttribute(string code) { } 17 | } 18 | 19 | internal class C99DeclCodeAttribute : System.Attribute 20 | { 21 | public C99DeclCodeAttribute(string code) { } 22 | } 23 | 24 | #pragma warning restore CA1018 25 | #nullable restore -------------------------------------------------------------------------------- /Core/Interfaces/ICommandListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Models; 2 | 3 | namespace SourceSharp.Core.Interfaces; 4 | 5 | internal interface ICommandListener : IListenerBase 6 | { 7 | /// 8 | /// ServerConsoleCommand 9 | /// 10 | /// 命令 11 | /// True = 已经被Hook且已调用 12 | bool OnServerConsoleCommand(ConsoleCommand command); 13 | 14 | /// 15 | /// ClientConsoleCommand 16 | /// 17 | /// 命令 18 | /// 调用命令的玩家 19 | /// True = 已经被Hook且已调用 20 | bool OnClientConsoleCommand(ConsoleCommand command, GamePlayer? player); 21 | } 22 | -------------------------------------------------------------------------------- /Core/Interfaces/IConVarManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | 3 | namespace SourceSharp.Core.Interfaces; 4 | 5 | internal interface IConVarManager : IListenerBase 6 | { 7 | public void OnConVarChanged(string name, string oldValue, string newValue); 8 | 9 | public CConVar? FindConVar(string name); 10 | } 11 | -------------------------------------------------------------------------------- /Core/Interfaces/IGameEventListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | 3 | namespace SourceSharp.Core.Interfaces; 4 | 5 | internal interface IGameEventListener : IListenerBase 6 | { 7 | /// 8 | /// 事件触发时调用 9 | /// 10 | /// CGameEvent 11 | /// True = 阻止事件 12 | bool OnEventFire(CGameEvent @event); 13 | 14 | /// 15 | /// 事件触发后调用 16 | /// 17 | /// CGameEvent 18 | void OnEventFired(CGameEvent @event); 19 | } 20 | -------------------------------------------------------------------------------- /Core/Interfaces/IListenerBase.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | 3 | namespace SourceSharp.Core.Interfaces; 4 | 5 | internal interface IListenerBase : IModuleBase 6 | { 7 | void OnPluginLoad(CPlugin plugin); 8 | void OnPluginUnload(CPlugin plugin); 9 | } 10 | -------------------------------------------------------------------------------- /Core/Interfaces/IModuleBase.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Interfaces; 2 | 3 | namespace SourceSharp.Core.Interfaces; 4 | 5 | internal interface IModuleBase : IRuntime 6 | { 7 | void Initialize(); 8 | void Shutdown(); 9 | } 10 | -------------------------------------------------------------------------------- /Core/Interfaces/IPlayerListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | using System.Net; 3 | 4 | namespace SourceSharp.Core.Interfaces; 5 | 6 | internal interface IPlayerListener : IListenerBase 7 | { 8 | /// 9 | /// ConnectHook - ProcessConnectionlessPacket 10 | /// 11 | /// SteamId 64 12 | /// Remote IP 13 | /// Remote Port 14 | /// Steam Name 15 | /// Connection Password 16 | /// 返回飞空值将会阻止链接 17 | string? OnConnectHook(ulong steamId, IPEndPoint endPoint, string name, string password); 18 | 19 | void OnConnected(CGamePlayer player); 20 | void OnAuthorized(CGamePlayer player); 21 | void OnPutInServer(CGamePlayer player); 22 | void OnPostAdminCheck(CGamePlayer player); 23 | void OnDisconnecting(CGamePlayer player); 24 | void OnDisconnected(CGamePlayer player); 25 | } 26 | -------------------------------------------------------------------------------- /Core/Interfaces/IPlayerManagerBase.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Interfaces; 2 | using System.Net; 3 | 4 | namespace SourceSharp.Core.Interfaces; 5 | 6 | internal interface IPlayerManagerBase : IPlayerManager 7 | { 8 | void OnConnected(int clientIndex, int userId, ulong steamId, string name, IPEndPoint remoteEndpoint); 9 | void OnAuthorized(int clientIndex, ulong steamId); 10 | void OnPutInServer(int clientIndex, bool fakeClient, bool sourceTv, bool replay); 11 | void OnDisconnecting(int clientIndex); 12 | void OnDisconnected(int clientIndex); 13 | void UpdatePlayerName(int clientIndex, string playerName); 14 | void OnNetChannelChanged(int clientIndex); 15 | } 16 | -------------------------------------------------------------------------------- /Core/Interfaces/IPluginManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | using SourceSharp.Sdk.Interfaces; 3 | using System; 4 | 5 | namespace SourceSharp.Core.Interfaces; 6 | 7 | internal interface IPluginManager 8 | { 9 | void Initialize(IServiceProvider services); 10 | void Shutdown(); 11 | void Signal(); 12 | 13 | // invoker 14 | void OnGameFrame(bool simulating); 15 | 16 | // native 17 | CPlugin? FindPlugin(IPlugin plugin); 18 | } 19 | -------------------------------------------------------------------------------- /Core/Interfaces/IShareSystemBase.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Interfaces; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace SourceSharp.Core.Interfaces; 6 | 7 | internal interface IShareSystemBase : IShareSystem 8 | { 9 | List CheckUnloadPluginInterfaces(IPlugin plugin); 10 | } 11 | 12 | public abstract class ShareSystemBase : IShareSystemBase 13 | { 14 | public virtual void AddInterface(IRuntime @interface, IPlugin @plugin) 15 | { 16 | throw new NotImplementedException(); 17 | } 18 | 19 | public virtual T GetRequiredInterface(uint version) where T : class, IRuntime 20 | { 21 | throw new NotImplementedException(); 22 | } 23 | 24 | public virtual T? GetInterface(uint version) where T : class, IRuntime 25 | { 26 | throw new NotImplementedException(); 27 | } 28 | 29 | public virtual List CheckUnloadPluginInterfaces(IPlugin plugin) 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | } -------------------------------------------------------------------------------- /Core/Interfaces/ISourceSharpBase.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | using SourceSharp.Sdk; 3 | using SourceSharp.Sdk.Enums; 4 | using SourceSharp.Sdk.Interfaces; 5 | using SourceSharp.Sdk.Models; 6 | using System; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Reflection; 10 | using CoreBridge = SourceSharp.Core.Bridges.SourceSharp; 11 | using EventBridge = SourceSharp.Core.Bridges.Event; 12 | 13 | namespace SourceSharp.Core.Interfaces; 14 | 15 | internal interface ISourceSharpBase : ISourceSharp 16 | { 17 | /// 18 | /// 运行帧 19 | /// 20 | void RunFrame(); 21 | } 22 | 23 | internal abstract class SourceSharpBase : ISourceSharpBase 24 | { 25 | /* 26 | * IRuntime 27 | */ 28 | public string GetInterfaceName() => SharedDefines.CoreInterfaceName; 29 | private readonly uint _versionNumber = (uint)Assembly.GetExecutingAssembly()!.GetName()!.Version!.Major; 30 | public uint GetInterfaceVersion() => _versionNumber; 31 | 32 | /* 33 | * ISourceSharp 34 | */ 35 | public abstract void Invoke(Action action); 36 | public abstract void InvokeNextFrame(Action action); 37 | 38 | public string BuildPath(PathType type, params string[] format) 39 | { 40 | if (!format.Any()) 41 | { 42 | throw new ArgumentException("Invalid format path!"); 43 | } 44 | 45 | string finalPath; 46 | switch (type) 47 | { 48 | case PathType.SourceSharpAbsolute: 49 | { 50 | var ssDir = GetRootPath(); 51 | finalPath = ssDir; 52 | foreach (var p in format) 53 | { 54 | finalPath = Path.Combine(finalPath, p); 55 | } 56 | 57 | break; 58 | } 59 | case PathType.Game: 60 | { 61 | var gameDir = GetGamePath(); 62 | finalPath = gameDir; 63 | foreach (var p in format) 64 | { 65 | finalPath = Path.Combine(finalPath, p); 66 | } 67 | 68 | break; 69 | } 70 | case PathType.None: 71 | { 72 | finalPath = format[0]; 73 | for (var i = 1; i < format.Length; i++) 74 | { 75 | finalPath = Path.Combine(finalPath, format[i]); 76 | } 77 | 78 | break; 79 | } 80 | default: 81 | { 82 | throw new ArgumentException("Invalid PathType given."); 83 | } 84 | } 85 | 86 | return finalPath; 87 | } 88 | 89 | public string GetGamePath() 90 | => CoreBridge.GetGamePath(); 91 | 92 | public string GetRootPath() 93 | => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; 94 | 95 | public void LogError(string message) 96 | { 97 | PrintError(message); 98 | CoreBridge.LogError(message); 99 | } 100 | 101 | public void LogMessage(string message) 102 | { 103 | PrintLog(message); 104 | CoreBridge.LogMessage(message); 105 | } 106 | 107 | public void PrintLine(string message) 108 | => Console.WriteLine(message); 109 | 110 | public virtual int GetMaxClients() 111 | => CoreBridge.GetMaxClients(); 112 | 113 | public virtual int GetMaxHumanPlayers() 114 | => CoreBridge.GetMaxHumanPlayers(); 115 | 116 | public GameEngineVersion GetEngineVersion() 117 | => (GameEngineVersion)CoreBridge.GetEngineVersion(); 118 | 119 | public void ExecuteServerCommand(string command) 120 | => CoreBridge.ExecuteServerCommand(command); 121 | 122 | public void InsertServerCommand(string command) 123 | => CoreBridge.InsertServerCommand(command); 124 | 125 | public void ServerExecute() 126 | => CoreBridge.ServerExecute(); 127 | 128 | public abstract ConVar? FindConVar(string name); 129 | 130 | public GameEvent CreateEvent(string name, bool broadcast) 131 | { 132 | var ev = EventBridge.CreateGameEvent(name, broadcast); 133 | return new CGameEvent(ev, false); 134 | } 135 | 136 | /* 137 | * ISourceSharpBase 138 | */ 139 | 140 | public abstract void RunFrame(); 141 | 142 | protected virtual void PrintError(string message) 143 | { 144 | var color = Console.ForegroundColor; 145 | Console.ForegroundColor = ConsoleColor.Red; 146 | Console.Write("[Fail] "); 147 | Console.ForegroundColor = ConsoleColor.Cyan; 148 | Console.Write($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} [SourceSharp] "); 149 | Console.ForegroundColor = color; 150 | Console.WriteLine(message); 151 | } 152 | 153 | protected virtual void PrintLog(string message) 154 | { 155 | var color = Console.ForegroundColor; 156 | Console.ForegroundColor = ConsoleColor.Yellow; 157 | Console.Write("[Info] "); 158 | Console.ForegroundColor = ConsoleColor.Cyan; 159 | Console.Write($"{DateTime.Now:yyyy/MM/dd HH:mm:ss} [SourceSharp] "); 160 | Console.ForegroundColor = color; 161 | Console.WriteLine(message); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /Core/Invoker.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SourceSharp.Core.Bridges; 3 | using SourceSharp.Core.Interfaces; 4 | using SourceSharp.Core.Models; 5 | using SourceSharp.Sdk.Models; 6 | using System; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Runtime.InteropServices; 10 | using System.Text; 11 | 12 | namespace SourceSharp.Core; 13 | 14 | internal static class Invoker 15 | { 16 | #nullable disable 17 | private static IPluginManager _pluginManager; 18 | private static ISourceSharpBase _sourceSharp; 19 | private static ICommandListener _commandListener; 20 | private static IPlayerManagerBase _playerManager; 21 | private static IPlayerListener _playerListener; 22 | private static IGameEventListener _gameEventListener; 23 | private static IConVarManager _conVarManager; 24 | #nullable restore 25 | 26 | internal static void Initialize(IServiceProvider services) 27 | { 28 | _pluginManager = services.GetRequiredService(); 29 | _sourceSharp = services.GetRequiredService(); 30 | _commandListener = services.GetRequiredService(); 31 | _playerManager = services.GetRequiredService(); 32 | _playerListener = services.GetRequiredService(); 33 | //_gameEventListener = services.GetRequiredService(); 34 | _conVarManager = services.GetRequiredService(); 35 | } 36 | 37 | /* 38 | * Bool 不是可导出/导入的类型! 39 | */ 40 | 41 | [UnmanagedCallersOnly] 42 | public static void OnGameFrame( 43 | [DNNE.C99Type("uint8_t")] sbyte simulating, 44 | [DNNE.C99Type("int32_t")] int tickCount, 45 | [DNNE.C99Type("float")] float gameTime) 46 | { 47 | _sourceSharp.RunFrame(); 48 | _pluginManager.OnGameFrame(simulating > 0); 49 | } 50 | 51 | #region Command Listener 52 | 53 | [UnmanagedCallersOnly] 54 | public static int ServerConsoleCommand( 55 | [DNNE.C99Type("const char*")] IntPtr pArgString, 56 | [DNNE.C99Type("const char**")] IntPtr pArgs, 57 | [DNNE.C99Type("int32_t")] int argc) 58 | { 59 | var command = ParseCommand(pArgString, pArgs, argc); 60 | if (command is null) 61 | { 62 | return 1; 63 | } 64 | _commandListener.OnServerConsoleCommand(command); 65 | return 0; 66 | } 67 | 68 | [UnmanagedCallersOnly] 69 | public static int ClientConsoleCommand( 70 | [DNNE.C99Type("const char*")] IntPtr pArgString, 71 | [DNNE.C99Type("const char**")] IntPtr pArgs, 72 | [DNNE.C99Type("int32_t")] int argc, 73 | [DNNE.C99Type("int32_t")] int clientIndex) 74 | { 75 | var command = ParseCommand(pArgString, pArgs, argc); 76 | if (command is null) 77 | { 78 | return 1; 79 | } 80 | 81 | GamePlayer? player = null; 82 | 83 | if (clientIndex > 0) 84 | { 85 | player = _playerManager.GetGamePlayer(clientIndex); 86 | if (player is null) 87 | { 88 | return 2; 89 | } 90 | } 91 | 92 | _commandListener.OnClientConsoleCommand(command, player); 93 | return 0; 94 | } 95 | 96 | private static ConsoleCommand? ParseCommand(IntPtr pArgString, IntPtr pArgs, int argc) 97 | { 98 | var argString = Marshal.PtrToStringAnsi(pArgString); 99 | 100 | if (argString is null || argc < 1) 101 | { 102 | _sourceSharp.LogError("Failed to parse command: ArgS or ArgC is invalid"); 103 | return null; 104 | } 105 | 106 | var args = Enumerable.Range(0, argc).Select(index => 107 | { 108 | if (Marshal.PtrToStructure(IntPtr.Add(pArgs, index * sizeof(int)), typeof(IntPtr)) is IntPtr strPtr) 109 | { 110 | var result = Marshal.PtrToStringAnsi(strPtr); 111 | 112 | if (result is not null) 113 | { 114 | return result; 115 | } 116 | } 117 | 118 | return string.Empty; 119 | }).ToArray(); 120 | 121 | return new ConsoleCommand(argString, args, argc); 122 | } 123 | 124 | #endregion 125 | 126 | #region ConVar 127 | 128 | [UnmanagedCallersOnly] 129 | public static void OnConVarChanged([DNNE.C99Type("const char*")] IntPtr pName, 130 | [DNNE.C99Type("const char*")] IntPtr pOldValue, 131 | [DNNE.C99Type("const char*")] IntPtr pNewValue) 132 | { 133 | var name = Marshal.PtrToStringAnsi(pName); 134 | var oldV = Marshal.PtrToStringAnsi(pOldValue); 135 | var newV = Marshal.PtrToStringAnsi(pNewValue); 136 | 137 | if (name is null || oldV is null || newV is null) 138 | { 139 | // null 140 | return; 141 | } 142 | 143 | _conVarManager.OnConVarChanged(name, oldV, newV); 144 | } 145 | 146 | #endregion 147 | 148 | #region Event Listener 149 | 150 | [UnmanagedCallersOnly] 151 | public static int OnFireEvent([DNNE.C99Type("const void*")] IntPtr pEvent) 152 | { 153 | var @event = SSGameEvent.__CreateInstance(pEvent); 154 | return _gameEventListener.OnEventFire(new CGameEvent(@event, false)) ? 1 : 0; 155 | } 156 | 157 | [UnmanagedCallersOnly] 158 | public static void OnFiredEvent([DNNE.C99Type("const void*")] IntPtr pEvent) 159 | { 160 | var @event = SSGameEvent.__CreateInstance(pEvent); 161 | _gameEventListener.OnEventFired(new CGameEvent(@event, true)); 162 | } 163 | 164 | #endregion 165 | 166 | #region Player Manager 167 | 168 | [UnmanagedCallersOnly] 169 | public static int OnClientConnected( 170 | [DNNE.C99Type("int32_t")] int clientIndex, 171 | [DNNE.C99Type("int32_t")] int userId, 172 | [DNNE.C99Type("uint64_t")] ulong steamId, 173 | [DNNE.C99Type("const char*")] IntPtr pName, 174 | [DNNE.C99Type("const char*")] IntPtr pINetAdr /* "114.114.114.114:12356" */) 175 | { 176 | var name = Marshal.PtrToStringAnsi(pName); 177 | var iNet = Marshal.PtrToStringAnsi(pINetAdr); 178 | if (name is null || iNet is null) 179 | { 180 | return 1; 181 | } 182 | 183 | if (!IPEndPoint.TryParse(iNet, out var ipEp)) 184 | { 185 | return 2; 186 | } 187 | 188 | _playerManager.OnConnected(clientIndex, userId, steamId, name, ipEp); 189 | return 0; 190 | } 191 | 192 | [UnmanagedCallersOnly] 193 | public static void OnAuthorized( 194 | [DNNE.C99Type("int32_t")] int clientIndex, 195 | [DNNE.C99Type("uint64_t")] ulong steamId) 196 | { 197 | _playerManager.OnAuthorized(clientIndex, steamId); 198 | } 199 | 200 | [UnmanagedCallersOnly] 201 | public static void OnPutInServer( 202 | [DNNE.C99Type("int32_t")] int clientIndex, 203 | [DNNE.C99Type("uint8_t")] sbyte fakeClient, 204 | [DNNE.C99Type("uint8_t")] sbyte sourceTv, 205 | [DNNE.C99Type("uint8_t")] sbyte replay) 206 | { 207 | _playerManager.OnPutInServer(clientIndex, fakeClient > 0, sourceTv > 0, replay > 0); 208 | } 209 | 210 | [UnmanagedCallersOnly] 211 | public static void OnDisconnecting([DNNE.C99Type("int32_t")] int clientIndex) 212 | { 213 | _playerManager.OnDisconnecting(clientIndex); 214 | } 215 | 216 | [UnmanagedCallersOnly] 217 | public static void OnDisconnected([DNNE.C99Type("int32_t")] int clientIndex) 218 | { 219 | _playerManager.OnDisconnected(clientIndex); 220 | } 221 | 222 | [UnmanagedCallersOnly] 223 | public static void OnNameChanged( 224 | [DNNE.C99Type("int32_t")] int clientIndex, 225 | [DNNE.C99Type("const char*")] IntPtr pName) 226 | { 227 | var name = Marshal.PtrToStringAnsi(pName); 228 | if (name == null) 229 | { 230 | return; 231 | } 232 | _playerManager.UpdatePlayerName(clientIndex, name); 233 | } 234 | 235 | [UnmanagedCallersOnly] 236 | public static void OnNetChannelChanged([DNNE.C99Type("int32_t")] int clientIndex) 237 | { 238 | _playerManager.OnNetChannelChanged(clientIndex); 239 | } 240 | 241 | #endregion 242 | 243 | #region Player Listener 244 | 245 | /// 246 | /// Connect Hook 247 | /// 248 | /// 0 = Accept | 1 = reject | 2 = block 249 | [UnmanagedCallersOnly] 250 | public static int OnConnectHook( 251 | [DNNE.C99Type("uint64_t")] ulong steamId, 252 | [DNNE.C99Type("const char*")] IntPtr pINetAdr, 253 | [DNNE.C99Type("const char*")] IntPtr pName, 254 | [DNNE.C99Type("const char*")] IntPtr pPassword, 255 | [DNNE.C99Type("char*")] IntPtr pRejectMessage, 256 | [DNNE.C99Type("int32_t")] int rejectMessageLength) 257 | { 258 | var name = Marshal.PtrToStringAnsi(pName); 259 | var iNet = Marshal.PtrToStringAnsi(pINetAdr); 260 | var pswd = Marshal.PtrToStringAnsi(pPassword); 261 | if (name is null || iNet is null || pswd is null) 262 | { 263 | return 2; 264 | } 265 | 266 | if (!IPEndPoint.TryParse(iNet, out var ipEp)) 267 | { 268 | return 2; 269 | } 270 | 271 | var rejectMessage = _playerListener.OnConnectHook(steamId, ipEp, name, pswd); 272 | 273 | if (rejectMessage is null) 274 | { 275 | // accept 276 | return 0; 277 | } 278 | 279 | var bytes = Encoding.Default.GetBytes(rejectMessage); 280 | if (bytes.Length > rejectMessageLength) 281 | { 282 | return 2; 283 | } 284 | 285 | Marshal.Copy(bytes, 0, pRejectMessage, rejectMessageLength); 286 | return 1; 287 | } 288 | 289 | #endregion 290 | } -------------------------------------------------------------------------------- /Core/Models/CAdmin.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using SourceSharp.Sdk.Models; 3 | 4 | namespace SourceSharp.Core.Models; 5 | 6 | internal sealed class CAdmin : AdminUser 7 | { 8 | private readonly uint _id; 9 | private readonly string _name; 10 | private readonly ulong _steamId; 11 | private AdminFlags _flags; 12 | 13 | public CAdmin(ulong steamId, string name, uint serial) 14 | { 15 | _steamId = steamId; 16 | _name = name; 17 | _id = serial; 18 | _flags = AdminFlags.None; 19 | } 20 | 21 | /* 22 | * Impl 23 | */ 24 | protected override uint GetId() => _id; 25 | protected override string GetName() => _name; 26 | protected override ulong GetSteamId() => _steamId; 27 | protected override AdminFlags GetFlags() => _flags; 28 | 29 | public override void SetAdminFlags(AdminFlags flags) => _flags = flags; 30 | public override void AddAdminFlags(AdminFlags flags) => _flags |= flags; 31 | public override void RemoveAdminFlags(AdminFlags flags) => _flags &= ~flags; 32 | public override bool HasFlags(AdminFlags flags) => _flags.HasFlag(flags); 33 | } 34 | -------------------------------------------------------------------------------- /Core/Models/CConVar.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Bridges; 2 | using SourceSharp.Sdk.Enums; 3 | using SourceSharp.Sdk.Interfaces; 4 | using SourceSharp.Sdk.Models; 5 | using SourceSharp.Sdk.Structs; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using ConVar = SourceSharp.Sdk.Models.ConVar; 10 | 11 | namespace SourceSharp.Core.Models; 12 | 13 | internal record ConVarHook(IPlugin Plugin, Action Callback); 14 | 15 | internal sealed class CConVar : ConVar 16 | { 17 | 18 | private readonly SSConVar _conVar; 19 | private readonly string _name; 20 | private readonly string _description; 21 | private readonly List _callbacks; 22 | 23 | public CConVar(SSConVar conVar, string name, string description) 24 | { 25 | _conVar = conVar; 26 | _name = name; 27 | _description = description; 28 | 29 | _callbacks = new(); 30 | } 31 | 32 | public override T Get() 33 | { 34 | if (typeof(T) == typeof(int)) 35 | { 36 | return (T)Convert.ChangeType(_conVar.Int, typeof(T)); 37 | } 38 | if (typeof(T) == typeof(bool)) 39 | { 40 | return (T)Convert.ChangeType(_conVar.Bool, typeof(T)); 41 | } 42 | if (typeof(T) == typeof(float)) 43 | { 44 | return (T)Convert.ChangeType(_conVar.Float, typeof(T)); 45 | } 46 | if (typeof(T) == typeof(string)) 47 | { 48 | return (T)Convert.ChangeType(_conVar.String, typeof(T)); 49 | } 50 | 51 | throw new InvalidCastException($"Bad ValueType<{typeof(T).FullName}> for event."); 52 | } 53 | 54 | public override void Set(T value) 55 | { 56 | switch (value) 57 | { 58 | case int iV: 59 | _conVar.Int = iV; 60 | break; 61 | case bool bV: 62 | _conVar.Bool = bV; 63 | break; 64 | case float flV: 65 | _conVar.Float = flV; 66 | break; 67 | case string sV: 68 | _conVar.String = sV; 69 | break; 70 | default: 71 | throw new InvalidCastException($"Bad ValueType<{typeof(T).FullName}> for event."); 72 | } 73 | } 74 | 75 | public override bool ReplicateToPlayers(GamePlayer[] players) 76 | { 77 | var playerIndex = players 78 | .Where(x => x is { IsValid: true, IsFakeClient: false, IsSourceTv: false, IsReplay: false }) 79 | .Select(x => x.Index) 80 | .ToArray(); 81 | 82 | return _conVar.ReplicateToPlayers(playerIndex, playerIndex.Length); 83 | } 84 | 85 | public override void AddFlags(ConVarFlags flags) => _conVar.AddFlags((int)flags); 86 | 87 | public override void RegisterChangeHook(IPlugin plugin, Action callback) 88 | { 89 | Bridges.ConVar.RegisterConVarHook(_name); 90 | _callbacks.Add(new ConVarHook(plugin, callback)); 91 | } 92 | 93 | internal void Invoke(string oldValue, string newValue) => 94 | _callbacks.ForEach(hook => 95 | { 96 | // TODO plugin status check 97 | 98 | try 99 | { 100 | hook.Callback.Invoke(this, oldValue, newValue); 101 | } 102 | catch { } 103 | }); 104 | 105 | protected override string GetName() => _name; 106 | 107 | protected override string GetDescription() => _description; 108 | 109 | protected override string GetDefaultValue() => _conVar.Default; 110 | 111 | protected override ConVarFlags GetFlags() => (ConVarFlags)_conVar.Flags; 112 | 113 | protected override ConVarBounds GetBounds() => new(_conVar.MinValue, _conVar.MaxValue, _conVar.HasMin, _conVar.HasMax); 114 | 115 | protected override void SetBounds(ConVarBounds bounds) 116 | { 117 | #if false 118 | if (bounds.HasMin) 119 | { 120 | _conVar.HasMin = true; 121 | _conVar.Min = bounds.Min; 122 | } 123 | else 124 | { 125 | _conVar.HasMin = false; 126 | } 127 | 128 | if (bounds.HasMax) 129 | { 130 | _conVar.HasMax = true; 131 | _conVar.Max = bounds.Max; 132 | } 133 | else 134 | { 135 | _conVar.HasMax = false; 136 | } 137 | #endif 138 | 139 | // TODO 140 | throw new NotImplementedException(); 141 | } 142 | 143 | // Internal Api 144 | internal void RemoveHook(IPlugin iPlugin) 145 | { 146 | var hooks = _callbacks.Where(x => x.Plugin == iPlugin); 147 | foreach (var hook in hooks) 148 | { 149 | _callbacks.Remove(hook); 150 | } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Core/Models/CGameEvent.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Bridges; 2 | using SourceSharp.Sdk.Models; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace SourceSharp.Core.Models; 7 | 8 | internal class CGameEvent : GameEvent 9 | { 10 | private readonly Dictionary _values; 11 | private readonly SSGameEvent _event; 12 | private readonly string _eventName; 13 | private readonly bool _noEdit; 14 | private bool _broadcast; 15 | 16 | public CGameEvent(SSGameEvent @event, bool post) 17 | { 18 | _event = @event; 19 | _eventName = @event.Name; 20 | _broadcast = @event.Broadcast; 21 | 22 | _values = new(); 23 | _noEdit = post; 24 | } 25 | 26 | public override T Get(string key) 27 | { 28 | if (_values.TryGetValue(key, out var value)) 29 | { 30 | if (value is not T v) 31 | { 32 | throw new InvalidOperationException($"Key '{key}' is not type {typeof(T).Name}"); 33 | } 34 | 35 | return v; 36 | } 37 | 38 | T newValue; 39 | 40 | // Call from native 41 | if (typeof(T) == typeof(int)) 42 | { 43 | newValue = (T)Convert.ChangeType(_event.GetInt(key), typeof(T)); 44 | } 45 | else if (typeof(T) == typeof(bool)) 46 | { 47 | newValue = (T)Convert.ChangeType(_event.GetBool(key), typeof(T)); 48 | } 49 | else if (typeof(T) == typeof(float)) 50 | { 51 | newValue = (T)Convert.ChangeType(_event.GetFloat(key), typeof(T)); 52 | } 53 | else if (typeof(T) == typeof(string)) 54 | { 55 | newValue = (T)Convert.ChangeType(_event.GetString(key), typeof(T)); 56 | } 57 | else 58 | { 59 | throw new InvalidCastException($"Bad ValueType<{typeof(T).FullName}> for event."); 60 | } 61 | 62 | _values[key] = newValue; 63 | 64 | return newValue; 65 | } 66 | 67 | public override bool Set(string key, T value) 68 | { 69 | CheckEditable(); 70 | 71 | var retV = value switch 72 | { 73 | int v => _event.SetInt(key, v), 74 | bool v => _event.SetBool(key, v), 75 | float v => _event.SetFloat(key, v), 76 | string v => _event.SetString(key, v), 77 | _ => throw new InvalidCastException($"Bad ValueType<{typeof(T).FullName}> for event.") 78 | }; 79 | 80 | if (retV) 81 | { 82 | _values[key] = value; 83 | } 84 | 85 | return retV; 86 | } 87 | 88 | public override void Cancel() => _event.Cancel(); 89 | 90 | protected override string GetName() => _eventName; 91 | protected override bool GetBroadcast() => _broadcast; 92 | protected override bool SetBroadcast(bool broadcast) 93 | { 94 | CheckEditable(); 95 | 96 | var ret = _event.SetBroadcast(broadcast); 97 | if (ret) 98 | { 99 | _broadcast = broadcast; 100 | } 101 | 102 | return ret; 103 | } 104 | 105 | private void CheckEditable() 106 | { 107 | if (_noEdit) 108 | { 109 | throw new InvalidOperationException("Event can not change after fired."); 110 | } 111 | } 112 | 113 | public override int GetHashCode() => _event.__Instance.GetHashCode(); 114 | public override bool Equals(object? obj) 115 | { 116 | if (obj is CGameEvent e) 117 | { 118 | return e.GetHashCode() == GetHashCode(); 119 | } 120 | 121 | return false; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /Core/Models/CGamePlayer.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using SourceSharp.Sdk.Models; 3 | using System.Net; 4 | 5 | namespace SourceSharp.Core.Models; 6 | 7 | internal sealed class CGamePlayer : GamePlayer 8 | { 9 | private static uint SerialNumber; 10 | 11 | // construct only 12 | private readonly ulong _steamId; 13 | private readonly uint _serial; 14 | private readonly int _userId; 15 | private readonly int _index; 16 | 17 | // state 18 | private bool _disconnected; 19 | private bool _disconnecting; 20 | private bool _isInGame; 21 | private bool _isAuthorized; 22 | private bool _isFakeClient; 23 | private bool _isSourceTv; 24 | private bool _isReplay; 25 | 26 | // variables 27 | private string _name; 28 | private IPEndPoint _ipEndpoint; 29 | private AdminFlags _adminFlags; 30 | 31 | // internal 32 | private bool _inKickQueue; 33 | 34 | // public 35 | public bool IsAdminChecked { get; private set; } 36 | 37 | 38 | public CGamePlayer(ulong steamId, int userId, int index, string name, IPEndPoint address) 39 | { 40 | _steamId = steamId; 41 | _serial = ++SerialNumber; // self increase 42 | _userId = userId; 43 | _index = index; 44 | 45 | _name = name; 46 | _ipEndpoint = address; 47 | _adminFlags = AdminFlags.None; 48 | 49 | _disconnected = false; 50 | _disconnecting = false; 51 | } 52 | 53 | public override void Kick(string message) 54 | { 55 | if (_inKickQueue) 56 | { 57 | return; 58 | } 59 | 60 | _inKickQueue = true; 61 | 62 | // "kickid %d %s\n" 63 | } 64 | 65 | public override void Print(string message) 66 | { 67 | // engine->ClientPrintf(m_pEdict, pMsg); 68 | } 69 | 70 | public override void TextMsg(string message) 71 | { 72 | /* 73 | #define TEXTMSG_DEST_NOTIFY 1 74 | #define TEXTMSG_DEST_CONSOLE 2 75 | #define TEXTMSG_DEST_CHAT 3 76 | #define TEXTMSG_DEST_CENTER 4 77 | */ 78 | } 79 | 80 | public bool Authorize(ulong steamId) 81 | { 82 | if (steamId != _steamId) 83 | { 84 | return false; 85 | } 86 | 87 | _isAuthorized = true; 88 | return true; 89 | } 90 | 91 | public void PutInGame(bool fakeClient, bool sourceTv, bool replay) 92 | { 93 | _isFakeClient = fakeClient; 94 | _isSourceTv = sourceTv; 95 | _isReplay = replay; 96 | 97 | _isInGame = true; 98 | } 99 | 100 | public void Disconnecting() => _disconnecting = true; 101 | 102 | public void Disconnect() 103 | { 104 | _isInGame = false; 105 | _disconnecting = false; 106 | _disconnected = true; 107 | } 108 | 109 | public void InvalidateAdmin() => _adminFlags = AdminFlags.None; 110 | 111 | public void AdminCheck(AdminFlags flags) 112 | { 113 | IsAdminChecked = true; 114 | _adminFlags = flags; 115 | } 116 | 117 | public void ChangeName(string name) => _name = name; 118 | 119 | /* 120 | * Impl 121 | */ 122 | protected override ulong GetSteamId() => _steamId; 123 | protected override uint GetSerial() => _serial; 124 | protected override int GetUserId() => _userId; 125 | protected override int GetIndex() => _index; 126 | protected override string GetName() => _name; 127 | protected override IPEndPoint GetRemoteEndPoint() => _ipEndpoint; 128 | protected override AdminFlags GetAdminFlags() => _adminFlags; 129 | protected override bool GetIsDisconnecting() => _disconnecting; 130 | protected override bool GetIsDisconnected() => _disconnected; 131 | protected override bool GetIsInGame() => _isInGame; 132 | protected override bool GetIsAuthorized() => _isAuthorized; 133 | protected override bool GetIsFakeClient() => _isFakeClient; 134 | protected override bool GetIsSourceTv() => _isSourceTv; 135 | protected override bool GetIsReplay() => _isReplay; 136 | } 137 | -------------------------------------------------------------------------------- /Core/Models/CHookCallback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | 4 | namespace SourceSharp.Core.Models; 5 | 6 | internal abstract class CHookCallback where T : Delegate 7 | { 8 | public CPlugin Plugin { get; } 9 | public T Callback { get; } 10 | 11 | protected CHookCallback(CPlugin plugin, MethodInfo method) 12 | { 13 | Plugin = plugin; 14 | Callback = method.CreateDelegate(plugin.Instance); 15 | } 16 | } -------------------------------------------------------------------------------- /Core/Models/CKeyHook.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Attributes; 2 | using SourceSharp.Sdk.Enums; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Reflection; 7 | 8 | namespace SourceSharp.Core.Models; 9 | 10 | /// 11 | /// 使用Key的Hook类型 12 | /// 13 | /// Hook Key 14 | /// Hook中保存的相关信息 15 | /// 要扫描的Attribute 16 | /// Hook调用时的返回值类型 17 | /// Callback 18 | internal class CKeyHook 19 | where TKey : IConvertible 20 | where TInfo : struct 21 | where TAttribute : HookBaseAttribute 22 | where TCallback : Delegate 23 | { 24 | internal sealed class ParamsHook : CHookCallback 25 | { 26 | public TInfo Info { get; } 27 | 28 | internal ParamsHook(CPlugin plugin, TInfo info, MethodInfo method) : base(plugin, method) 29 | { 30 | Info = info; 31 | } 32 | } 33 | 34 | private readonly Dictionary> _hooks; 35 | 36 | public CKeyHook() => _hooks = new(); 37 | 38 | /// 39 | /// 清理Hook 40 | /// 41 | public void Shutdown() => _hooks.Clear(); 42 | 43 | /// 44 | /// 扫描插件是否使用Hook 45 | /// 46 | /// CPlugin实例 47 | /// 转换Attribute数据到Info实体的Lambda方法 48 | /// 注册机 49 | public void ScanPlugin(CPlugin plugin, Func convertInfo, Action? register) 50 | { 51 | var hooks = plugin.Instance.GetType() 52 | .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy) 53 | .Where(m => m.GetCustomAttributes(typeof(TAttribute), false).Any()) 54 | .ToList(); 55 | 56 | if (!hooks.Any()) 57 | { 58 | return; 59 | } 60 | 61 | foreach (var hook in hooks) 62 | { 63 | var attrs = Attribute.GetCustomAttributes(hook, typeof(TAttribute)); 64 | if (!attrs.Any()) 65 | { 66 | continue; 67 | } 68 | 69 | foreach (var vt in attrs) 70 | { 71 | if (vt is not TAttribute t) 72 | { 73 | continue; 74 | } 75 | 76 | if (Subscribe(t.Key, plugin, convertInfo(t), hook)) 77 | { 78 | register?.Invoke(t.Key); 79 | } 80 | } 81 | } 82 | } 83 | 84 | /// 85 | /// 订阅Hook 86 | /// 87 | /// Key 88 | /// CPlugin实例 89 | /// HookInfo 90 | /// 方法 91 | /// 92 | private bool Subscribe(TKey key, CPlugin plugin, TInfo info, MethodInfo method) 93 | { 94 | var create = false; 95 | if (!_hooks.ContainsKey(key)) 96 | { 97 | _hooks[key] = new(); 98 | create = true; 99 | } 100 | 101 | _hooks[key].Add(new(plugin, info, method)); 102 | return create; 103 | } 104 | 105 | /// 106 | /// 注销插件 107 | /// 108 | /// 109 | public void RemovePlugin(CPlugin plugin) 110 | { 111 | foreach (var (key, hooks) in _hooks.Where(x => x.Value.Any(v => v.Plugin == plugin))) 112 | { 113 | for (var i = 0; i < hooks.Count; i++) 114 | { 115 | if (hooks[i].Plugin.Equals(plugin)) 116 | { 117 | hooks.RemoveAt(i); 118 | i--; 119 | } 120 | } 121 | 122 | if (!hooks.Any()) 123 | { 124 | _hooks.Remove(key); 125 | break; 126 | } 127 | } 128 | } 129 | 130 | /// 131 | /// Hook调用时 132 | /// 133 | /// Hook Key 134 | /// 默认返回值 (在没有相关Hook时直接返回) 135 | /// 迭代器 136 | /// 迭代器中的返回值 137 | public TCallReturn OnCall(TKey key, TCallReturn defaultReturn, Func, TCallReturn> iterator) 138 | => !_hooks.TryGetValue(key, out var hooks) || !hooks.Any(x => x.Plugin.Status is PluginStatus.Running) 139 | ? defaultReturn 140 | : iterator.Invoke(hooks.Where(x => x.Plugin.Status is PluginStatus.Running)); 141 | } 142 | -------------------------------------------------------------------------------- /Core/Models/CPlugin.cs: -------------------------------------------------------------------------------- 1 | using McMaster.NETCore.Plugins; 2 | using SourceSharp.Sdk.Attributes; 3 | using SourceSharp.Sdk.Enums; 4 | using SourceSharp.Sdk.Interfaces; 5 | using System; 6 | using System.Reflection; 7 | using System.Text.Json; 8 | 9 | namespace SourceSharp.Core.Models; 10 | 11 | internal sealed class CPlugin 12 | { 13 | public string Path { get; } 14 | 15 | public PluginLoader Loader { get; } 16 | 17 | public IPlugin Instance { get; } 18 | 19 | public PluginStatus Status { get; private set; } 20 | 21 | public Action? FrameHook { get; private set; } 22 | 23 | // copyright 24 | public string Name { get; } 25 | public string Author { get; } 26 | public string Version { get; } 27 | public string Url { get; } 28 | public string Description { get; } 29 | 30 | public CPlugin(string path, PluginLoader loader, IPlugin instance, PluginAttribute pa) 31 | { 32 | Path = path; 33 | Loader = loader; 34 | Instance = instance; 35 | Status = PluginStatus.Checked; 36 | 37 | Name = pa.Name; 38 | Author = pa.Author; 39 | Version = pa.Version; 40 | Url = pa.Url; 41 | Description = pa.Description; 42 | } 43 | 44 | public void UpdateStatus(PluginStatus status) => Status = status; 45 | public void AddGameHook(MethodInfo method) => FrameHook = method.CreateDelegate>(Instance); 46 | 47 | public override string ToString() 48 | => JsonSerializer.Serialize(new { Name, Author, Version, Url, Description }, new JsonSerializerOptions { WriteIndented = true }); 49 | 50 | public override int GetHashCode() 51 | => Path.GetHashCode(); 52 | 53 | public override bool Equals(object? obj) 54 | { 55 | if (obj is CPlugin p) 56 | { 57 | return p.GetHashCode() == GetHashCode(); 58 | } 59 | 60 | return false; 61 | } 62 | 63 | public static bool operator ==(CPlugin obj1, CPlugin obj2) 64 | => obj1.Equals(obj2); 65 | public static bool operator !=(CPlugin obj1, CPlugin obj2) 66 | => !obj1.Equals(obj2); 67 | } 68 | -------------------------------------------------------------------------------- /Core/Modules/AdminManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Models; 2 | using SourceSharp.Sdk; 3 | using SourceSharp.Sdk.Interfaces; 4 | using SourceSharp.Sdk.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | 9 | namespace SourceSharp.Core.Modules; 10 | 11 | internal sealed class AdminManager : IAdminManager 12 | { 13 | private readonly List _admins; 14 | private uint _serialNumber; 15 | 16 | public AdminManager() 17 | { 18 | _admins = new(); 19 | _serialNumber = 0; 20 | } 21 | 22 | /* 23 | * AdminManager 24 | */ 25 | public AdminUser CreateAdmin(ulong steamId, string name) 26 | { 27 | if (_admins.Any(x => x.SteamId == steamId)) 28 | { 29 | throw new InvalidOperationException($"SteamId {steamId} is already admin!"); 30 | } 31 | 32 | var admin = new CAdmin(steamId, name, ++_serialNumber); 33 | _admins.Add(admin); 34 | 35 | return admin; 36 | } 37 | 38 | public void RemoveAdmin(AdminUser adminUser) 39 | { 40 | if (adminUser is not CAdmin admin) 41 | { 42 | throw new ArgumentException("instance is not admin"); 43 | } 44 | 45 | if (!_admins.Contains(admin)) 46 | { 47 | throw new InvalidOperationException("instance is not admin"); 48 | } 49 | 50 | _admins.Remove(admin); 51 | } 52 | 53 | public IReadOnlyList GetAdmins() => _admins.AsReadOnly(); 54 | 55 | public AdminUser? FindAdminByIdentity(ulong steamId) => _admins.SingleOrDefault(x => x.SteamId == steamId); 56 | 57 | /* 58 | * IRuntime 59 | */ 60 | public string GetInterfaceName() => SharedDefines.AdminManagerInterfaceName; 61 | public uint GetInterfaceVersion() => SharedDefines.AdminManagerInterfaceVersion; 62 | } 63 | -------------------------------------------------------------------------------- /Core/Modules/CommandListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Core.Models; 3 | using SourceSharp.Sdk; 4 | using SourceSharp.Sdk.Attributes; 5 | using SourceSharp.Sdk.Enums; 6 | using SourceSharp.Sdk.Interfaces; 7 | using SourceSharp.Sdk.Models; 8 | using System; 9 | 10 | namespace SourceSharp.Core.Modules; 11 | 12 | internal sealed class CommandListener : ICommandListener 13 | { 14 | private readonly struct ConsoleCommandInfo 15 | { 16 | public string Command { get; } 17 | public string Description { get; } 18 | public ConVarFlags Flags { get; } 19 | public AdminFlags AccessFlags { get; } 20 | 21 | public ConsoleCommandInfo(string command, string description, ConVarFlags flags, AdminFlags accessFlags) 22 | { 23 | Command = command; 24 | Description = description; 25 | Flags = flags; 26 | AccessFlags = accessFlags; 27 | } 28 | } 29 | 30 | private readonly CKeyHook> _server; 35 | 36 | private readonly CKeyHook> _client; 41 | 42 | private readonly IAdminManager _adminManager; 43 | 44 | public CommandListener(IAdminManager adminManager) 45 | { 46 | _client = new(); 47 | _server = new(); 48 | 49 | _adminManager = adminManager; 50 | } 51 | 52 | public void Initialize() 53 | { 54 | // do nothing 55 | } 56 | 57 | public void Shutdown() 58 | { 59 | _server.Shutdown(); 60 | _client.Shutdown(); 61 | } 62 | 63 | public void OnPluginLoad(CPlugin plugin) 64 | { 65 | _client.ScanPlugin(plugin, 66 | attr => new ConsoleCommandInfo(attr.Key, attr.Description, attr.Flags, AdminFlags.None), 67 | Bridges.ConCommand.RegClientCommand); 68 | 69 | _server.ScanPlugin(plugin, 70 | attr => new ConsoleCommandInfo(attr.Key, attr.Description, attr.Flags, AdminFlags.None), 71 | Bridges.ConCommand.RegServerCommand); 72 | } 73 | 74 | public void OnPluginUnload(CPlugin plugin) 75 | { 76 | _client.RemovePlugin(plugin); 77 | _server.RemovePlugin(plugin); 78 | } 79 | 80 | public bool OnClientConsoleCommand(ConsoleCommand command, GamePlayer? player) 81 | => _client.OnCall(command.Command, false, hooks => 82 | { 83 | // 这里与SourceMod不同 84 | // SM中注册命令的权限是以第一个注册的代码为准 85 | // 在SS中的实现为根据每个Attribute中的Flags 86 | // 来确定能否调用对应的Action 87 | 88 | var accessFlags = AdminFlags.None; 89 | 90 | AdminUser? admin; 91 | if (player is CGamePlayer { IsAuthorized: true } cp && (admin = _adminManager.FindAdminByIdentity(cp.SteamId)) is not null) 92 | { 93 | accessFlags = admin.Flags; 94 | } 95 | 96 | foreach (var hook in hooks) 97 | { 98 | if (accessFlags.HasFlag(hook.Info.AccessFlags)) 99 | { 100 | hook.Callback.Invoke(command, player); 101 | } 102 | } 103 | 104 | return true; 105 | }); 106 | 107 | public bool OnServerConsoleCommand(ConsoleCommand command) 108 | => _server.OnCall(command.Command, false, hooks => 109 | { 110 | foreach (var hook in hooks) 111 | { 112 | hook.Callback.Invoke(command); 113 | } 114 | return true; 115 | }); 116 | 117 | /* 118 | * IRuntime 119 | */ 120 | public string GetInterfaceName() => SharedDefines.CommandListenerInterfaceName; 121 | public uint GetInterfaceVersion() => SharedDefines.CommandListenerInterfaceVersion; 122 | } 123 | -------------------------------------------------------------------------------- /Core/Modules/ConVarManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Core.Models; 3 | using SourceSharp.Core.Utils; 4 | using SourceSharp.Sdk; 5 | using SourceSharp.Sdk.Attributes; 6 | using SourceSharp.Sdk.Enums; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Reflection; 11 | using ConVar = SourceSharp.Sdk.Models.ConVar; 12 | using ConVarBridge = SourceSharp.Core.Bridges.ConVar; 13 | 14 | namespace SourceSharp.Core.Modules; 15 | 16 | internal class ConVarManager : IConVarManager 17 | { 18 | private readonly struct ConVarHookInfo 19 | { 20 | public CConVar ConVar { get; } 21 | public ConVarHookInfo(CConVar cvar) => ConVar = cvar; 22 | } 23 | 24 | private sealed class ConVarHook : CHookCallback> 25 | { 26 | public CConVar ConVar { get; } 27 | 28 | internal ConVarHook(CPlugin plugin, CConVar conVar, MethodInfo method) : base(plugin, method) 29 | { 30 | ConVar = conVar; 31 | } 32 | } 33 | 34 | private readonly CKeyHook> _hooks; 39 | 40 | private readonly List _conVars; 41 | 42 | private readonly ISourceSharpBase _sourceSharp; 43 | private readonly IPluginManager _pluginManager; 44 | 45 | public ConVarManager(ISourceSharpBase sourceSharp, IPluginManager pluginManager) 46 | { 47 | _sourceSharp = sourceSharp; 48 | _pluginManager = pluginManager; 49 | 50 | _conVars = new(); 51 | _hooks = new(); 52 | } 53 | 54 | public void Initialize() 55 | { 56 | // do nothing 57 | } 58 | 59 | public void Shutdown() 60 | { 61 | _conVars.Clear(); 62 | _hooks.Shutdown(); 63 | } 64 | 65 | public void OnPluginLoad(CPlugin plugin) 66 | { 67 | RegConVars(plugin); 68 | HookConVars(plugin); 69 | } 70 | 71 | public void OnPluginUnload(CPlugin plugin) 72 | { 73 | _hooks.RemovePlugin(plugin); 74 | _conVars.ForEach(conVar => conVar.RemoveHook(plugin.Instance)); 75 | } 76 | 77 | public void OnConVarChanged(string name, string oldValue, string newValue) 78 | { 79 | var conVar = _conVars.Find(x => x.Name == name); 80 | if (conVar is null) 81 | { 82 | return; 83 | } 84 | 85 | _hooks.OnCall(conVar.Name, false, hooks => 86 | { 87 | foreach (var hook in hooks) 88 | { 89 | hook.Callback.Invoke(hook.Info.ConVar, oldValue, newValue); 90 | } 91 | 92 | return true; 93 | }); 94 | } 95 | 96 | public CConVar? FindConVar(string name) 97 | { 98 | var conVar = _conVars.Find(x => x.Name == name); 99 | if (conVar is null) 100 | { 101 | var iCvar = ConVarBridge.FindConVar(name); 102 | if (iCvar is null) 103 | { 104 | // does not exists 105 | return null; 106 | } 107 | conVar = new(iCvar, iCvar.Name, iCvar.Description); 108 | } 109 | return conVar; 110 | } 111 | 112 | private void RegConVars(CPlugin plugin) 113 | { 114 | var conVars = plugin.Instance.GetType() 115 | .GetProperties(BindingFlags.Public | BindingFlags.Instance) 116 | .Where(m => Attribute.GetCustomAttributes(m, typeof(ConVarAttribute)).Any()) 117 | .ToList(); 118 | 119 | if (!conVars.Any()) 120 | { 121 | return; 122 | } 123 | 124 | foreach (var cvarAttr in conVars) 125 | { 126 | if (Attribute.GetCustomAttribute(cvarAttr, typeof(ConVarAttribute)) is not ConVarAttribute cvar) 127 | { 128 | continue; 129 | } 130 | 131 | var iCvar = ConVarBridge.CreateConVar(cvar.Name, cvar.DefaultValue, 132 | cvar.Description, (int)cvar.Flags, 133 | cvar.HasMin, cvar.Min, cvar.HasMax, cvar.Max); 134 | 135 | if (iCvar is null) 136 | { 137 | _sourceSharp.LogError($"Failed to create ConVar: {cvar.Name}"); 138 | plugin.UpdateStatus(PluginStatus.Error); 139 | return; 140 | } 141 | 142 | var conVar = new CConVar(iCvar, iCvar.Name, iCvar.Description); 143 | plugin.Instance.GetType().SetReadonlyProperty(cvarAttr.Name, plugin.Instance, conVar); 144 | _conVars.Add(conVar); 145 | } 146 | } 147 | 148 | private void HookConVars(CPlugin plugin) 149 | => _hooks.ScanPlugin(plugin, 150 | attr => 151 | { 152 | var conVar = _conVars.Find(x => x.Name == attr.Name); 153 | if (conVar is null) 154 | { 155 | // lookup game ConVar 156 | var iCvar = ConVarBridge.FindConVar(attr.Name) ?? 157 | throw new InvalidOperationException( 158 | $"Failed to find ConVar: [{attr.Name}], make sure ConVar does exists."); 159 | 160 | conVar = new CConVar(iCvar, iCvar.Name, iCvar.Description); 161 | _conVars.Add(conVar); 162 | } 163 | 164 | return new ConVarHookInfo(conVar); 165 | }, 166 | name => ConVarBridge.RegisterConVarHook(name)); 167 | 168 | /* 169 | * IRuntime 170 | */ 171 | public string GetInterfaceName() => SharedDefines.ConVarManagerInterfaceName; 172 | public uint GetInterfaceVersion() => SharedDefines.ConVarManagerInterfaceVersion; 173 | 174 | } 175 | -------------------------------------------------------------------------------- /Core/Modules/GameEventListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Core.Models; 3 | using SourceSharp.Sdk; 4 | using SourceSharp.Sdk.Attributes; 5 | using SourceSharp.Sdk.Enums; 6 | using SourceSharp.Sdk.Models; 7 | using SourceSharp.Sdk.Structs; 8 | using System; 9 | using System.Linq; 10 | 11 | namespace SourceSharp.Core.Modules; 12 | 13 | internal sealed class GameEventListener : IGameEventListener 14 | { 15 | private readonly struct EventListenerInfo 16 | { 17 | public GameEventHookType HookType { get; } = GameEventHookType.Post; 18 | public int Priority { get; } = 0; 19 | 20 | public EventListenerInfo(GameEventHookType hookType, int priority) 21 | { 22 | HookType = hookType; 23 | Priority = priority; 24 | } 25 | } 26 | 27 | private readonly CKeyHook>> _hooks; 32 | 33 | public GameEventListener() 34 | { 35 | _hooks = new(); 36 | } 37 | 38 | public void Initialize() 39 | { 40 | // Do nothing 41 | } 42 | 43 | public void Shutdown() 44 | { 45 | _hooks.Shutdown(); 46 | } 47 | 48 | public void OnPluginLoad(CPlugin plugin) 49 | { 50 | _hooks.ScanPlugin(plugin, 51 | attribute => new(attribute.Type, attribute.Priority), 52 | s => Bridges.Event.RegGameEventHook(s)); 53 | } 54 | 55 | public void OnPluginUnload(CPlugin plugin) 56 | => _hooks.RemovePlugin(plugin); 57 | 58 | public bool OnEventFire(CGameEvent @event) 59 | => _hooks.OnCall(@event.Name, false, hooks => 60 | { 61 | var code = 0; 62 | var block = false; 63 | 64 | foreach (var listener in hooks 65 | .Where(x => x.Info.HookType is GameEventHookType.Pre) 66 | .OrderByDescending(x => x.Info.Priority)) 67 | { 68 | var response = listener.Callback.Invoke(@event); 69 | if (response.Code > code) 70 | { 71 | code = response.Code; 72 | block = response.Response; 73 | } 74 | } 75 | 76 | return block; 77 | }); 78 | 79 | public void OnEventFired(CGameEvent @event) 80 | => _hooks.OnCall(@event.Name, false, hooks => 81 | { 82 | foreach (var listener in hooks 83 | .Where(x => x.Info.HookType is GameEventHookType.Post) 84 | .OrderByDescending(x => x.Info.Priority)) 85 | { 86 | listener.Callback.Invoke(@event); 87 | } 88 | 89 | return true; 90 | }); 91 | 92 | /* 93 | * IRuntime 94 | */ 95 | public string GetInterfaceName() => SharedDefines.GameEventListenerInterfaceName; 96 | public uint GetInterfaceVersion() => SharedDefines.GameEventListenerInterfaceVersion; 97 | } 98 | -------------------------------------------------------------------------------- /Core/Modules/PlayerListener.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Core.Models; 3 | using SourceSharp.Core.Utils; 4 | using SourceSharp.Sdk; 5 | using SourceSharp.Sdk.Attributes; 6 | using SourceSharp.Sdk.Enums; 7 | using SourceSharp.Sdk.Models; 8 | using SourceSharp.Sdk.Structs; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Net; 13 | using System.Reflection; 14 | 15 | namespace SourceSharp.Core.Modules; 16 | 17 | internal sealed class PlayerListener : IPlayerListener 18 | { 19 | private class PlayerEvent : CHookCallback> 20 | { 21 | internal PlayerEvent(CPlugin plugin, MethodInfo method) : base(plugin, method) { } 22 | } 23 | 24 | private class PlayerHook : CHookCallback>> 25 | { 26 | internal PlayerHook(CPlugin plugin, MethodInfo method) : base(plugin, method) { } 27 | } 28 | 29 | private readonly List _events; 30 | private readonly List _hooks; 31 | 32 | public PlayerListener() 33 | { 34 | _events = new(); 35 | _hooks = new(); 36 | } 37 | 38 | public void Initialize() 39 | { 40 | // Do nothing... 41 | } 42 | 43 | public void Shutdown() 44 | { 45 | _events.Clear(); 46 | _hooks.Clear(); 47 | } 48 | 49 | public void OnPluginLoad(CPlugin plugin) 50 | { 51 | var hooks = plugin.Instance.GetType().GetMethods() 52 | .Where(m => Attribute.GetCustomAttributes(m, typeof(PlayerListenerAttribute)).Any()) 53 | .ToList(); 54 | 55 | if (!hooks.Any()) 56 | { 57 | return; 58 | } 59 | 60 | foreach (var hook in hooks) 61 | { 62 | if (Attribute.GetCustomAttribute(hook, typeof(PlayerListenerAttribute)) is not PlayerListenerAttribute ev) 63 | { 64 | continue; 65 | } 66 | 67 | if (ev.Type is PlayerListenerType.ConnectHook) 68 | { 69 | hook.CheckReturnAndParameters(typeof(bool), new[] { typeof(ulong), typeof(string), typeof(string), typeof(string), typeof(string) }); 70 | _hooks.Add(new(plugin, hook)); 71 | } 72 | else 73 | { 74 | hook.CheckReturnAndParameters(typeof(void), new[] { typeof(CGamePlayer) }); 75 | _events.Add(new(plugin, hook)); 76 | } 77 | } 78 | } 79 | 80 | public void OnPluginUnload(CPlugin plugin) 81 | { 82 | _hooks.RemoveAll(x => x.Plugin.Equals(plugin)); 83 | _events.RemoveAll(x => x.Plugin.Equals(plugin)); 84 | } 85 | 86 | /* 87 | * Listener 88 | */ 89 | public string? OnConnectHook(ulong steamId, IPEndPoint remoteEndPoint, string name, string password) 90 | { 91 | string? returnValue = null; 92 | var code = 0; 93 | 94 | foreach (var hook in _hooks) 95 | { 96 | var response = hook.Callback.Invoke(steamId, remoteEndPoint, name, password); 97 | if (response.Code > code) 98 | { 99 | returnValue = response.Response; 100 | code = response.Code; 101 | } 102 | } 103 | 104 | return returnValue; 105 | } 106 | 107 | public void OnConnected(CGamePlayer player) 108 | { 109 | throw new NotImplementedException(); 110 | } 111 | 112 | public void OnAuthorized(CGamePlayer player) 113 | { 114 | throw new NotImplementedException(); 115 | } 116 | 117 | public void OnPutInServer(CGamePlayer player) 118 | { 119 | throw new NotImplementedException(); 120 | } 121 | 122 | public void OnPostAdminCheck(CGamePlayer player) 123 | { 124 | throw new NotImplementedException(); 125 | } 126 | 127 | public void OnDisconnecting(CGamePlayer player) 128 | { 129 | throw new NotImplementedException(); 130 | } 131 | 132 | public void OnDisconnected(CGamePlayer player) 133 | { 134 | throw new NotImplementedException(); 135 | } 136 | 137 | /* 138 | * IRuntime 139 | */ 140 | public string GetInterfaceName() => SharedDefines.PlayerListenerInterfaceName; 141 | public uint GetInterfaceVersion() => SharedDefines.PlayerListenerInterfaceVersion; 142 | } 143 | -------------------------------------------------------------------------------- /Core/Modules/PlayerManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Core.Models; 3 | using SourceSharp.Sdk; 4 | using SourceSharp.Sdk.Interfaces; 5 | using SourceSharp.Sdk.Models; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net; 9 | 10 | namespace SourceSharp.Core.Modules; 11 | 12 | internal sealed class PlayerManager : IPlayerManagerBase 13 | { 14 | private readonly ISourceSharpBase _sourceSharp; 15 | private readonly IPlayerListener _playerListener; 16 | private readonly IAdminManager _adminManager; 17 | 18 | // TODO GetMaxClients 19 | private readonly List _players; 20 | 21 | public PlayerManager(ISourceSharpBase sourceSharp, IPlayerListener playerListener, IAdminManager adminManager) 22 | { 23 | _sourceSharp = sourceSharp; 24 | _playerListener = playerListener; 25 | _adminManager = adminManager; 26 | 27 | _players = new(64); 28 | } 29 | 30 | /* 31 | * IPlayerManagerBase 32 | */ 33 | public void OnConnected(int clientIndex, int userId, ulong steamId, string name, IPEndPoint remoteEndpoint) 34 | { 35 | var player = new CGamePlayer(steamId, userId, clientIndex, name, remoteEndpoint); 36 | _players.Add(player); 37 | _playerListener.OnConnected(player); 38 | } 39 | 40 | public void OnAuthorized(int clientIndex, ulong steamId) 41 | { 42 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 43 | if (player is null) 44 | { 45 | _sourceSharp.LogError($"Authorized failed: player index {clientIndex} not found"); 46 | return; 47 | } 48 | 49 | if (!player.Authorize(steamId)) 50 | { 51 | _sourceSharp.LogError($"Authorized failed: player index {clientIndex} steamId is spoofed"); 52 | player.Kick("SteamId Spoof!"); 53 | return; 54 | } 55 | 56 | _playerListener.OnAuthorized(player); 57 | 58 | if (player is { IsInGame: true, IsAdminChecked: false }) 59 | { 60 | RunAdminCheck(player, true); 61 | } 62 | } 63 | 64 | public void OnPutInServer(int clientIndex, bool fakeClient, bool sourceTv, bool replay) 65 | { 66 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 67 | if (player is null) 68 | { 69 | _sourceSharp.LogError($"PutInServer failed: player index {clientIndex} not found"); 70 | return; 71 | } 72 | 73 | player.PutInGame(fakeClient, sourceTv, replay); 74 | 75 | _playerListener.OnPutInServer(player); 76 | 77 | if (player is { IsAuthorized: true, IsAdminChecked: false }) 78 | { 79 | RunAdminCheck(player, true); 80 | } 81 | } 82 | 83 | public void OnDisconnecting(int clientIndex) 84 | { 85 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 86 | if (player is null) 87 | { 88 | _sourceSharp.LogError($"Disconnecting failed: player index {clientIndex} not found"); 89 | return; 90 | } 91 | 92 | player.Disconnecting(); 93 | 94 | _playerListener.OnDisconnecting(player); 95 | } 96 | 97 | public void OnDisconnected(int clientIndex) 98 | { 99 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 100 | if (player is null) 101 | { 102 | _sourceSharp.LogError($"Disconnected failed: player index {clientIndex} not found"); 103 | return; 104 | } 105 | 106 | player.Disconnect(); 107 | 108 | _playerListener.OnDisconnected(player); 109 | 110 | _players.Remove(player); 111 | } 112 | 113 | public void UpdatePlayerName(int clientIndex, string playerName) 114 | { 115 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 116 | if (player is null) 117 | { 118 | _sourceSharp.LogError($"UpdateName failed: player index {clientIndex} not found"); 119 | return; 120 | } 121 | 122 | // update name cache 123 | player.ChangeName(playerName); 124 | } 125 | 126 | public void OnNetChannelChanged(int clientIndex) 127 | { 128 | var player = _players.SingleOrDefault(x => x.Index == clientIndex); 129 | if (player is null) 130 | { 131 | _sourceSharp.LogError($"NetChannelChanged failed: player index {clientIndex} not found"); 132 | return; 133 | } 134 | 135 | player.Kick("网络环境发生变化, 请重新连接!"); 136 | } 137 | 138 | /* 139 | * Internal 140 | */ 141 | private void RunAdminCheck(CGamePlayer player, bool call) 142 | { 143 | player.InvalidateAdmin(); 144 | 145 | var adminUser = _adminManager.FindAdminByIdentity(player.SteamId); 146 | if (adminUser is CAdmin admin) 147 | { 148 | player.AdminCheck(admin.Flags); 149 | } 150 | 151 | if (call) 152 | { 153 | _playerListener.OnPostAdminCheck(player); 154 | } 155 | } 156 | 157 | /* 158 | * IPlayerManager 159 | */ 160 | public uint GetNumPlayers() => (uint)_players.Count; 161 | public uint GetMaxClients() => (uint)_players.Capacity; 162 | public IReadOnlyList GetClients() => _players.AsReadOnly(); 163 | public IReadOnlyList GetPlayers() => _players.FindAll(x => x is { IsSourceTv: false, IsReplay: false }).AsReadOnly(); 164 | public GamePlayer? GetGamePlayerByUserId(int userId) => _players.SingleOrDefault(x => x.UserId == userId); 165 | public GamePlayer? GetGamePlayer(uint serial) => _players.SingleOrDefault(x => x.Serial == serial); 166 | public GamePlayer? GetGamePlayer(int index) => _players.SingleOrDefault(x => x.Index == index); 167 | public void RunAdminChecks() => _players.ForEach(p => RunAdminCheck(p, false)); 168 | 169 | /* 170 | * IRuntime 171 | */ 172 | public string GetInterfaceName() => SharedDefines.PlayerManagerInterfaceName; 173 | public uint GetInterfaceVersion() => SharedDefines.PlayerManagerInterfaceVersion; 174 | } 175 | -------------------------------------------------------------------------------- /Core/PluginManager.cs: -------------------------------------------------------------------------------- 1 | using McMaster.NETCore.Plugins; 2 | using SourceSharp.Core.Interfaces; 3 | using SourceSharp.Core.Models; 4 | using SourceSharp.Core.Utils; 5 | using SourceSharp.Sdk.Attributes; 6 | using SourceSharp.Sdk.Enums; 7 | using SourceSharp.Sdk.Interfaces; 8 | using System; 9 | using System.Collections.Generic; 10 | using System.IO; 11 | using System.Linq; 12 | using System.Reflection; 13 | 14 | namespace SourceSharp.Core; 15 | 16 | internal sealed class PluginManager : IPluginManager 17 | { 18 | private readonly ISourceSharpBase _sourceSharp; 19 | private readonly IShareSystemBase _shareSystem; 20 | 21 | private readonly List _plugins; 22 | private readonly List _listeners; 23 | 24 | public PluginManager(ISourceSharpBase sourceSharp, IShareSystemBase shareSystem) 25 | { 26 | _sourceSharp = sourceSharp; 27 | _shareSystem = shareSystem; 28 | 29 | _plugins = new(); 30 | _listeners = new(); 31 | } 32 | 33 | public void Initialize(IServiceProvider services) 34 | { 35 | var listeners = services.GetAllServices(); 36 | _listeners.AddRange(listeners); 37 | 38 | var sourceSharpRoot = Path.Combine(_sourceSharp.GetRootPath(), _sourceSharp.GetGamePath(), "addons", "sourcesharp"); 39 | 40 | foreach (var path in Directory.GetDirectories(Path.Combine(sourceSharpRoot, "plugins"), "*", SearchOption.TopDirectoryOnly)) 41 | { 42 | CPlugin? plugin = null; 43 | var name = Path.GetFileName(path); 44 | try 45 | { 46 | var file = Path.Combine(path, $"{name}.dll"); 47 | 48 | if (!File.Exists(file)) 49 | { 50 | throw new DllNotFoundException($"Plugin dll not found."); 51 | } 52 | 53 | var absolutePath = Path.GetFullPath(file); 54 | 55 | var loader = PluginLoader.CreateFromAssemblyFile(absolutePath, config => 56 | { 57 | config.PreferSharedTypes = true; 58 | config.IsUnloadable = true; 59 | config.LoadInMemory = true; 60 | }); 61 | 62 | var tPlugin = loader.LoadDefaultAssembly().GetTypes() 63 | .SingleOrDefault(t => typeof(PluginBase).IsAssignableFrom(t) && !t.IsAbstract) ?? 64 | throw new FileLoadException("IPlugin is not implemented."); 65 | 66 | if (Activator.CreateInstance(tPlugin) is not PluginBase instance) 67 | { 68 | throw new InvalidOperationException("Failed to create instance."); 69 | } 70 | 71 | var pa = Attribute.GetCustomAttribute(tPlugin, typeof(PluginAttribute)) as PluginAttribute ?? 72 | throw new BadImageFormatException("Plugin metadata not found"); 73 | 74 | plugin = new CPlugin(file, loader, instance, pa); 75 | _plugins.Add(plugin); 76 | 77 | tPlugin.SetProtectedReadOnlyField("_sourceSharp", instance, _sourceSharp); 78 | tPlugin.SetProtectedReadOnlyField("_shareSystem", instance, _shareSystem); 79 | 80 | var frameHooks = tPlugin 81 | .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy) 82 | .Where(m => m.GetCustomAttributes(typeof(GameFrameAttribute), false).Any()) 83 | .ToList(); 84 | 85 | if (frameHooks.Any()) 86 | { 87 | if (frameHooks.Count > 1) 88 | { 89 | throw new BadImageFormatException("Multiple GameFrameHook found."); 90 | } 91 | 92 | var frameHook = frameHooks.Single(); 93 | frameHook.CheckReturnAndParameters(typeof(void), new[] { typeof(bool) }); 94 | plugin.AddGameHook(frameHook); 95 | } 96 | 97 | foreach (var listener in _listeners) 98 | { 99 | listener.OnPluginLoad(plugin); 100 | } 101 | } 102 | catch (Exception e) 103 | { 104 | plugin?.UpdateStatus(PluginStatus.Failed); 105 | _sourceSharp.LogError($"Failed to load plugin <{name}>: {e.Message}{Environment.NewLine}{e.StackTrace}"); 106 | } 107 | } 108 | 109 | LoadPlugins(); 110 | } 111 | 112 | public void Signal() 113 | { 114 | foreach (var plugin in _plugins.Where(x => x.Status == PluginStatus.Running)) 115 | { 116 | try 117 | { 118 | if (!plugin.Instance.QueryRunning()) 119 | { 120 | throw new InvalidOperationException(); 121 | } 122 | } 123 | catch (Exception e) 124 | { 125 | plugin.UpdateStatus(PluginStatus.Error); 126 | 127 | if (e is not InvalidOperationException) 128 | { 129 | _sourceSharp.LogMessage($"Failed to load plugin <{plugin.Name}>: {e.Message}{Environment.NewLine}{e.StackTrace}"); 130 | } 131 | } 132 | } 133 | } 134 | 135 | public void Shutdown() 136 | { 137 | foreach (var plugin in _plugins) 138 | { 139 | UnloadPlugin(plugin, false); 140 | } 141 | 142 | _plugins.Clear(); 143 | } 144 | 145 | public void OnGameFrame(bool simulating) 146 | { 147 | foreach (var plugin in _plugins.Where(x => x.Status is PluginStatus.Running && x.FrameHook is not null)) 148 | { 149 | plugin.FrameHook?.Invoke(simulating); 150 | } 151 | } 152 | 153 | private void LoadPlugins() 154 | { 155 | foreach (var plugin in _plugins.Where(x => x.Status is PluginStatus.Checked)) 156 | { 157 | LoadPlugin(plugin); 158 | } 159 | } 160 | 161 | private void LoadPlugin(CPlugin plugin) 162 | { 163 | try 164 | { 165 | if (!plugin.Instance.OnLoad()) 166 | { 167 | throw new InvalidOperationException(); 168 | } 169 | 170 | plugin.UpdateStatus(PluginStatus.Running); 171 | _sourceSharp.PrintLine($"Plugin <{plugin.Name}> loaded."); 172 | } 173 | catch (Exception e) 174 | { 175 | plugin.UpdateStatus(PluginStatus.Failed); 176 | 177 | if (e is not InvalidOperationException) 178 | { 179 | _sourceSharp.LogMessage($"Failed to load plugin <{plugin.Name}>: {e.Message}{Environment.NewLine}{e.StackTrace}"); 180 | } 181 | } 182 | } 183 | 184 | private void UnloadPlugin(CPlugin plugin, bool remove) 185 | { 186 | try 187 | { 188 | var interfaces = _shareSystem.CheckUnloadPluginInterfaces(plugin.Instance); 189 | if (interfaces.Any()) 190 | { 191 | foreach (var @interface in interfaces) 192 | { 193 | foreach (var p in _plugins.Where(x => !x.Equals(plugin))) 194 | { 195 | p.Instance.NotifyInterfaceDrop(@interface); 196 | } 197 | } 198 | } 199 | 200 | foreach (var listener in _listeners) 201 | { 202 | listener.OnPluginUnload(plugin); 203 | } 204 | 205 | plugin.Instance.OnShutdown(); 206 | plugin.Loader.Dispose(); 207 | plugin.UpdateStatus(PluginStatus.None); 208 | 209 | if (remove) 210 | { 211 | _plugins.Remove(plugin); 212 | } 213 | } 214 | catch (Exception e) 215 | { 216 | _sourceSharp.LogError($"Error during shutting down on <{plugin.Name}>: {e.Message}{Environment.NewLine}{e.StackTrace}"); 217 | } 218 | } 219 | 220 | public CPlugin? FindPlugin(IPlugin plugin) 221 | => _plugins.SingleOrDefault(x => x.Instance == plugin); 222 | } -------------------------------------------------------------------------------- /Core/ShareSystem.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Core.Interfaces; 2 | using SourceSharp.Sdk.Interfaces; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | 7 | namespace SourceSharp.Core; 8 | 9 | internal sealed class ShareSystem : ShareSystemBase 10 | { 11 | private class SharedInterface 12 | { 13 | public IRuntime Instance { get; } 14 | public IPlugin Plugin { get; } 15 | 16 | public SharedInterface(IRuntime instance, IPlugin plugin) 17 | { 18 | Instance = instance; 19 | Plugin = plugin; 20 | } 21 | } 22 | 23 | private readonly List _interfaces; 24 | 25 | public ShareSystem() 26 | { 27 | _interfaces = new(); 28 | } 29 | 30 | public override void AddInterface(IRuntime @interface, IPlugin @plugin) 31 | { 32 | var name = @interface.GetInterfaceName(); 33 | 34 | if (_interfaces.Any(x => x.Instance.GetInterfaceName() == name)) 35 | { 36 | throw new InvalidOperationException($"Interface with name {name} already exists!"); 37 | } 38 | 39 | _interfaces.Add(new(@interface, @plugin)); 40 | } 41 | 42 | public override T GetRequiredInterface(uint version) 43 | { 44 | var @interface = _interfaces.SingleOrDefault(x => x.GetType() == typeof(T)) ?? 45 | throw new NotImplementedException($"Interface <{nameof(T)}> not found."); 46 | 47 | if (@interface.Instance.GetInterfaceVersion() < version) 48 | { 49 | throw new NotImplementedException($"Interface <{nameof(T)}> version is lower."); 50 | } 51 | 52 | return (T)@interface.Instance; 53 | } 54 | 55 | public override T? GetInterface(uint version) where T : class 56 | => (T?)_interfaces.SingleOrDefault(x => x.GetType() == typeof(T) && x.Instance.GetInterfaceVersion() >= version)?.Instance; 57 | 58 | public override List CheckUnloadPluginInterfaces(IPlugin plugin) 59 | { 60 | var interfaces = _interfaces.Where(x => x.Plugin == plugin).ToList(); 61 | _interfaces.RemoveAll(x => x.Plugin == plugin); 62 | return interfaces.Select(x => x.Instance).ToList(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Core/SourceSharp.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using SourceSharp.Core.Interfaces; 3 | using SourceSharp.Sdk.Models; 4 | using System; 5 | using System.Collections.Concurrent; 6 | 7 | namespace SourceSharp.Core; 8 | 9 | internal sealed class SourceSharp : SourceSharpBase 10 | { 11 | private readonly int _masterThreadId; 12 | private readonly ConcurrentQueue _invokes; 13 | private readonly IServiceProvider _service; 14 | 15 | public SourceSharp(IServiceProvider service) 16 | { 17 | _masterThreadId = Environment.CurrentManagedThreadId; 18 | _invokes = new(); 19 | _service = service; 20 | } 21 | 22 | public override void RunFrame() 23 | { 24 | while (_invokes.TryDequeue(out var action)) 25 | { 26 | action.Invoke(); 27 | } 28 | } 29 | 30 | public override void Invoke(Action action) 31 | { 32 | if (Environment.CurrentManagedThreadId == _masterThreadId) 33 | { 34 | action.Invoke(); 35 | } 36 | else 37 | { 38 | _invokes.Enqueue(action); 39 | } 40 | } 41 | 42 | public override ConVar? FindConVar(string name) 43 | { 44 | var cm = _service.GetRequiredService(); 45 | return cm.FindConVar(name); 46 | } 47 | 48 | public override void InvokeNextFrame(Action action) 49 | => _invokes.Enqueue(action); 50 | } 51 | -------------------------------------------------------------------------------- /Core/Utils/Reflection.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Reflection; 6 | 7 | namespace SourceSharp.Core.Utils; 8 | 9 | internal static class Reflection 10 | { 11 | public static void CheckReturnAndParameters(this MethodInfo method, Type returnType, Type[] @paramsType) 12 | { 13 | if (method.ReturnParameter.ParameterType != typeof(void)) 14 | { 15 | throw new BadImageFormatException("Bad return value: " + returnType.Name); 16 | } 17 | 18 | var @params = method.GetParameters(); 19 | if (@params.Length != @paramsType.Length) 20 | { 21 | throw new BadImageFormatException("Parameters count mismatch"); 22 | } 23 | 24 | for (var i = 0; i < @paramsType.Length; i++) 25 | { 26 | var type = @params[i].ParameterType; 27 | if (type != @paramsType[i]) 28 | { 29 | throw new BadImageFormatException("Bad parameter type: " + type.Name); 30 | } 31 | } 32 | } 33 | 34 | public static void SetProtectedReadOnlyField(this Type type, string name, object instance, object value) 35 | { 36 | var field = type.GetField(name, BindingFlags.Instance | BindingFlags.NonPublic) 37 | ?? throw new MissingFieldException(type.FullName, name); 38 | 39 | field.SetValue(instance, value); 40 | } 41 | 42 | public static void SetReadonlyProperty(this Type type, string name, object instance, object value) 43 | { 44 | var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); 45 | 46 | var fName = $"<{name}>k__BackingField"; 47 | var field = type.GetField(fName, BindingFlags.Instance | BindingFlags.NonPublic) 48 | ?? throw new MissingFieldException(type.FullName, fName); 49 | 50 | field.SetValue(instance, value); 51 | } 52 | 53 | public static IEnumerable GetAllServices(this IServiceProvider provider) 54 | { 55 | var site = typeof(ServiceProvider).GetProperty("CallSiteFactory", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(provider)!; 56 | var desc = site.GetType().GetField("_descriptors", BindingFlags.Instance | BindingFlags.NonPublic)!.GetValue(site) as ServiceDescriptor[]; 57 | return desc!.Select(s => provider.GetRequiredService(s.ServiceType)).OfType(); 58 | } 59 | } -------------------------------------------------------------------------------- /Core/core.json: -------------------------------------------------------------------------------- 1 | { 2 | "Core": { 3 | "AllowUnsafe": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /PluginExample/Example.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk; 2 | using SourceSharp.Sdk.Attributes; 3 | using SourceSharp.Sdk.Enums; 4 | using SourceSharp.Sdk.Interfaces; 5 | using SourceSharp.Sdk.Models; 6 | using SourceSharp.Sdk.Structs; 7 | using System.Reflection; 8 | 9 | namespace SourceSharp.Example; 10 | 11 | #pragma warning disable IDE0051, IDE0052, IDE0060, IDE1006 12 | 13 | public interface IExportInterface : IRuntime 14 | { 15 | void TestExport(); 16 | } 17 | 18 | [Plugin(Name = "Example", Author = "SourceSharp Team", Version = "1.0")] 19 | public class Example : PluginBase, IExportInterface 20 | { 21 | [ConVar("ss_plugin_example", "0")] 22 | public ConVar ss_plugin_example { get; } = null!; 23 | 24 | [ConVar("test_convar", "0", 25 | Description = "测试", 26 | Flags = ConVarFlags.Notify | ConVarFlags.Replicated, 27 | HasMin = true, Min = 0f, HasMax = true, Max = 999f)] 28 | public ConVar test_convar { get; } = null!; 29 | 30 | public override bool OnLoad() 31 | { 32 | _sourceSharp.LogMessage("plugin loaded"); 33 | _shareSystem.AddInterface(this, this); 34 | 35 | ss_plugin_example.RegisterChangeHook(this, 36 | (var, oldValue, newValue) => 37 | _sourceSharp.LogMessage($"ConVar [{var.Name}] -> old<{oldValue}> -> new<{newValue}")); 38 | 39 | return true; 40 | } 41 | 42 | public override void OnAllLoad() 43 | { 44 | var export = _shareSystem.GetRequiredInterface(1); 45 | export.TestExport(); 46 | } 47 | 48 | public override void OnShutdown() 49 | => _sourceSharp.LogMessage("plugin unloaded"); 50 | 51 | [ClientConsoleCommand("say", "Say Command")] 52 | private void TestHookSayCommand(ConsoleCommand command, GamePlayer? player) 53 | => _sourceSharp.LogMessage("say command executed: " + command.ArgString); 54 | 55 | [ServerConsoleCommand("ss_s_test", "测试命令")] 56 | private void TestServerCommand(ConsoleCommand command) 57 | => _sourceSharp.LogMessage("test command executed: " + command.ArgString); 58 | 59 | [ClientConsoleCommand("ss_c_test", "测试命令", ConVarFlags.Release | ConVarFlags.ServerCanExecute, AdminFlags.ChangeMap)] 60 | private void TestClientCommand(ConsoleCommand command, GamePlayer? player) 61 | => _sourceSharp.LogMessage("test command executed: " + command.ArgString); 62 | 63 | [GameEvent("player_spawn")] 64 | private ActionResponse OnPlayerSpawn(GameEvent @event) 65 | { 66 | _sourceSharp.LogMessage("player spawned -> userId: " + @event.Get("userid")); 67 | return default; 68 | } 69 | 70 | [GameFrame] 71 | private void OnGameFrame(bool simulating) 72 | { 73 | if (test_convar.Get()) 74 | { 75 | _sourceSharp.LogMessage($"OnGameFrame({simulating})"); 76 | } 77 | } 78 | 79 | [PlayerListener(PlayerListenerType.Connected)] 80 | private void OnPlayerConnected(GamePlayer player) 81 | => _sourceSharp.LogMessage("player connected -> name: " + player.Name); 82 | 83 | [ConVarChanged("sv_cheats")] 84 | [ConVarChanged("mp_timelimit")] 85 | private void OnConVarChanged(ConVar conVar, string oldValue, string newValue) 86 | => _sourceSharp.LogMessage($"ConVarChanged -> {conVar.Name} = {newValue} (old: {oldValue})"); 87 | 88 | /* 89 | * Export 90 | */ 91 | public void TestExport() => _sourceSharp.LogMessage("Test Export"); 92 | public string GetInterfaceName() => SharedDefines.InterfacePrefix + Assembly.GetExecutingAssembly()!.GetName().Name!.ToUpper(); 93 | public uint GetInterfaceVersion() => (uint)Assembly.GetExecutingAssembly()!.GetName()!.Version!.Major; 94 | } -------------------------------------------------------------------------------- /PluginExample/PluginExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | disable 5 | 11 6 | enable 7 | SourceSharp.Example 8 | SourceSharp.Example 9 | 1.0 10 | 1 11 | $(VersionPrefix).$(VersionSuffix) 12 | https://github.com/SourceSharp 13 | ©2023 Kyle, Bone 14 | https://github.com/SourceSharp/runtime 15 | true 16 | 17 | 18 | True 19 | 20 | 21 | True 22 | 23 | 24 | True 25 | 26 | 27 | True 28 | 29 | 30 | True 31 | 32 | 33 | True 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SourceSharp - Runtime 2 | Runtime for SourceSharp script engine. 3 | -------------------------------------------------------------------------------- /Runtime.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33530.505 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core", "Core\Core.csproj", "{F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sdk", "Sdk\Sdk.csproj", "{42C1916F-71A2-443C-8AC1-0C46266865DA}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PluginExample", "PluginExample\PluginExample.csproj", "{C9FCEA87-EC02-42C1-9954-BDB4393026E8}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Core.Test", "Core.Test\Core.Test.csproj", "{81EA187C-BE91-4AFA-81EF-F60C56E423F4}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Debug|x64 = Debug|x64 18 | Debug|x86 = Debug|x86 19 | Release|Any CPU = Release|Any CPU 20 | Release|x64 = Release|x64 21 | Release|x86 = Release|x86 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|x64.Build.0 = Debug|Any CPU 28 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|x86.ActiveCfg = Debug|Any CPU 29 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Debug|x86.Build.0 = Debug|Any CPU 30 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|x64.ActiveCfg = Release|x64 33 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|x64.Build.0 = Release|x64 34 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|x86.ActiveCfg = Release|x86 35 | {F3EF0BDC-0C53-498F-BCF1-C3DADCC8B74D}.Release|x86.Build.0 = Release|x86 36 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|x64.ActiveCfg = Debug|Any CPU 39 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|x64.Build.0 = Debug|Any CPU 40 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Debug|x86.Build.0 = Debug|Any CPU 42 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|Any CPU.Build.0 = Release|Any CPU 44 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|x64.ActiveCfg = Release|x64 45 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|x64.Build.0 = Release|x64 46 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|x86.ActiveCfg = Release|x86 47 | {42C1916F-71A2-443C-8AC1-0C46266865DA}.Release|x86.Build.0 = Release|x86 48 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|x64.ActiveCfg = Debug|Any CPU 51 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|x64.Build.0 = Debug|Any CPU 52 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|x86.ActiveCfg = Debug|Any CPU 53 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Debug|x86.Build.0 = Debug|Any CPU 54 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|x64.ActiveCfg = Release|x64 57 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|x64.Build.0 = Release|x64 58 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|x86.ActiveCfg = Release|x86 59 | {C9FCEA87-EC02-42C1-9954-BDB4393026E8}.Release|x86.Build.0 = Release|x86 60 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|x64.ActiveCfg = Debug|Any CPU 63 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|x64.Build.0 = Debug|Any CPU 64 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|x86.ActiveCfg = Debug|Any CPU 65 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Debug|x86.Build.0 = Debug|Any CPU 66 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|x64.ActiveCfg = Release|Any CPU 69 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|x64.Build.0 = Release|Any CPU 70 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|x86.ActiveCfg = Release|Any CPU 71 | {81EA187C-BE91-4AFA-81EF-F60C56E423F4}.Release|x86.Build.0 = Release|Any CPU 72 | EndGlobalSection 73 | GlobalSection(SolutionProperties) = preSolution 74 | HideSolutionNode = FALSE 75 | EndGlobalSection 76 | GlobalSection(ExtensibilityGlobals) = postSolution 77 | SolutionGuid = {947B0431-4278-4234-A4DE-EC301676DBD2} 78 | EndGlobalSection 79 | EndGlobal 80 | -------------------------------------------------------------------------------- /Sdk/Attributes/ConVarAttribute.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using System; 3 | 4 | namespace SourceSharp.Sdk.Attributes; 5 | 6 | [AttributeUsage(AttributeTargets.Property)] 7 | public class ConVarAttribute : Attribute 8 | { 9 | public string Name { get; } 10 | public string DefaultValue { get; } 11 | public string Description { get; set; } = string.Empty; 12 | public ConVarFlags Flags { get; set; } = ConVarFlags.None; 13 | public bool HasMin { get; set; } 14 | public float Min { get; set; } 15 | public bool HasMax { get; set; } 16 | public float Max { get; set; } 17 | 18 | public ConVarAttribute(string name, string defaultValue) 19 | { 20 | Name = name.ToLower(); 21 | DefaultValue = defaultValue; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sdk/Attributes/ConVarChangedAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] 6 | public class ConVarChangedAttribute : HookBaseAttribute 7 | { 8 | public string Name => Key; 9 | 10 | public ConVarChangedAttribute(string name) : base(name.ToLower()) { } 11 | } 12 | -------------------------------------------------------------------------------- /Sdk/Attributes/ConsoleCommandAttribute.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using System; 3 | 4 | namespace SourceSharp.Sdk.Attributes; 5 | 6 | public abstract class ConsoleCommandBaseAttribute : HookBaseAttribute 7 | { 8 | public string Description { get; } 9 | public ConVarFlags Flags { get; } 10 | public string Command => Key; 11 | 12 | protected ConsoleCommandBaseAttribute(string command) : base(command.ToLower()) 13 | { 14 | Description = string.Empty; 15 | Flags = ConVarFlags.None; 16 | } 17 | 18 | protected ConsoleCommandBaseAttribute(string command, string description) : base(command.ToLower()) 19 | { 20 | Description = description; 21 | Flags = ConVarFlags.None; 22 | } 23 | 24 | protected ConsoleCommandBaseAttribute(string command, string description, ConVarFlags flags) : base(command.ToLower()) 25 | { 26 | Description = description; 27 | Flags = flags; 28 | } 29 | } 30 | 31 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 32 | public sealed class ServerConsoleCommandAttribute : ConsoleCommandBaseAttribute 33 | { 34 | public ServerConsoleCommandAttribute(string command) : base(command) { } 35 | 36 | public ServerConsoleCommandAttribute(string command, string description) : base(command, description) { } 37 | 38 | public ServerConsoleCommandAttribute(string command, string description, ConVarFlags flags) : base(command, description, flags) { } 39 | } 40 | 41 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 42 | public sealed class ClientConsoleCommandAttribute : ConsoleCommandBaseAttribute 43 | { 44 | public AdminFlags AccessFlags { get; } 45 | 46 | public ClientConsoleCommandAttribute(string command) : base(command) 47 | { 48 | AccessFlags = AdminFlags.None; 49 | } 50 | 51 | public ClientConsoleCommandAttribute(string command, string description) : base(command, description) 52 | { 53 | AccessFlags = AdminFlags.None; 54 | } 55 | 56 | public ClientConsoleCommandAttribute(string command, string description, ConVarFlags flags) : base(command, description, flags) 57 | { 58 | AccessFlags = AdminFlags.None; 59 | } 60 | 61 | public ClientConsoleCommandAttribute(string command, string description, ConVarFlags flags, AdminFlags admin) : base(command, description, flags) 62 | { 63 | AccessFlags = admin; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sdk/Attributes/GameEventAttribute.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using System; 3 | 4 | namespace SourceSharp.Sdk.Attributes; 5 | 6 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 7 | public sealed class GameEventAttribute : HookBaseAttribute 8 | { 9 | public string EventName => Key; 10 | public GameEventHookType Type { get; } 11 | public int Priority { get; set; } 12 | 13 | public GameEventAttribute(string name) : base(name.ToLower()) 14 | { 15 | Type = GameEventHookType.Post; 16 | Priority = 0; 17 | } 18 | 19 | public GameEventAttribute(string name, GameEventHookType type) : base(name.ToLower()) 20 | { 21 | Type = type; 22 | Priority = 0; 23 | } 24 | 25 | public GameEventAttribute(string name, GameEventHookType type, int priority) : base(name.ToLower()) 26 | { 27 | Type = type; 28 | Priority = int.Clamp(priority, 0, 100); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sdk/Attributes/GameFrameAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 6 | public sealed class GameFrameAttribute : Attribute 7 | { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /Sdk/Attributes/HookBaseAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Attributes; 4 | 5 | public abstract class HookBaseAttribute : Attribute where T : IConvertible 6 | { 7 | public T Key { get; } 8 | 9 | protected HookBaseAttribute(T key) 10 | { 11 | Key = key; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sdk/Attributes/PlayerListenerAttribute.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using System; 3 | 4 | namespace SourceSharp.Sdk.Attributes; 5 | 6 | [AttributeUsage(AttributeTargets.Method, Inherited = false)] 7 | public sealed class PlayerListenerAttribute : Attribute 8 | { 9 | public PlayerListenerType Type { get; } 10 | 11 | public PlayerListenerAttribute(PlayerListenerType type) 12 | => Type = type; 13 | } 14 | -------------------------------------------------------------------------------- /Sdk/Attributes/PluginAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 6 | public sealed class PluginAttribute : Attribute 7 | { 8 | public required string Name { get; set; } 9 | public required string Author { get; set; } 10 | public required string Version { get; set; } 11 | public string Url { get; set; } = "https://github.com/SourceSharp"; 12 | public string Description { get; set; } = string.Empty; 13 | } -------------------------------------------------------------------------------- /Sdk/Enums/AdminFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Enums; 4 | 5 | [Flags] 6 | public enum AdminFlags : uint 7 | { 8 | None = 0, 9 | 10 | /// 11 | /// Reserved slot 12 | /// 13 | Reservation = 1 << 0, 14 | 15 | /// 16 | /// Generic admin abilities 17 | /// 18 | Generic = 1 << 1, 19 | 20 | /// 21 | /// Kick another user 22 | /// 23 | Kick = 1 << 2, 24 | 25 | /// 26 | /// Ban another user 27 | /// 28 | Ban = 1 << 3, 29 | 30 | /// 31 | /// Unban another user 32 | /// 33 | Unban = 1 << 4, 34 | 35 | /// 36 | /// Slay/kill/damage another user 37 | /// 38 | Slay = 1 << 5, 39 | 40 | /// 41 | /// Change the map 42 | /// 43 | ChangeMap = 1 << 6, 44 | 45 | /// 46 | /// Change basic ConVar 47 | /// 48 | ConVar = 1 << 7, 49 | 50 | /// 51 | /// Change configuration 52 | /// 53 | Config = 1 << 8, 54 | 55 | /// 56 | /// Special chat privileges 57 | /// 58 | Chat = 1 << 9, 59 | 60 | /// 61 | /// Special vote privileges 62 | /// 63 | Vote = 1 << 10, 64 | 65 | /// 66 | /// Set a server password 67 | /// 68 | Password = 1 << 11, 69 | 70 | /// 71 | /// Use RCON 72 | /// 73 | RCon = 1 << 12, 74 | 75 | /// 76 | /// Change sv_cheats and use its commands 77 | /// 78 | Cheats = 1 << 13, 79 | 80 | /// 81 | /// All access by default 82 | /// 83 | Root = 1 << 14, 84 | 85 | Custom1 = 1 << 15, 86 | Custom2 = 1 << 16, 87 | Custom3 = 1 << 17, 88 | Custom4 = 1 << 18, 89 | Custom5 = 1 << 19, 90 | Custom6 = 1 << 20, 91 | } 92 | -------------------------------------------------------------------------------- /Sdk/Enums/ConVarFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Enums; 4 | 5 | [Flags] 6 | public enum ConVarFlags : int 7 | { 8 | /// 9 | /// No flags 10 | /// 11 | None, 12 | 13 | /* 14 | * Command to ConVars and ConCommands 15 | */ 16 | 17 | // ConVar Systems 18 | 19 | /// 20 | /// If this is set, don't add to linked list, etc. 21 | /// 22 | Unregistered = (1 << 0), 23 | 24 | /// 25 | /// Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. 26 | /// 27 | DevelopmentOnly = (1 << 1), 28 | 29 | /// 30 | /// defined by the game DLL 31 | /// 32 | GameDll = (1 << 2), 33 | 34 | /// 35 | /// defined by the client DLL 36 | /// 37 | ClientDll = (1 << 3), 38 | 39 | /// 40 | /// Hidden. Doesn't appear in find or auto complete. Like DEVELOPMENTONLY, but can't be compiled out. 41 | /// 42 | Hidden = (1 << 4), 43 | 44 | // ConVar only 45 | 46 | /// 47 | /// It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as value 48 | /// 49 | Protected = (1 << 5), 50 | 51 | /// 52 | /// This cvar cannot be changed by clients connected to a multiplayer server. 53 | /// 54 | SinglePlayerOnly = (1 << 6), 55 | 56 | /// 57 | /// set to cause it to be saved to vars.rc 58 | /// 59 | Archive = (1 << 7), 60 | 61 | /// 62 | /// notifies players when changed 63 | /// 64 | Notify = (1 << 8), 65 | 66 | /// 67 | /// changes the client's info string 68 | /// 69 | UserInfo = (1 << 9), 70 | 71 | /// 72 | /// This cvar's string cannot contain unprintable characters= ( e.g., used for player name etc ). 73 | /// 74 | PrintableOnly = (1 << 10), 75 | 76 | /// 77 | /// When on concommands this allows remote clients to execute this cmd on the server. 78 | /// We are changing the default behavior of concommands to disallow execution by remote clients without 79 | /// this flag due to the number existing concommands that can lag or crash the server when clients abuse them. 80 | /// 81 | GameDllForRemoteClients = PrintableOnly, 82 | 83 | /// 84 | /// If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log 85 | /// 86 | UnLogged = (1 << 11), 87 | 88 | /// 89 | /// never try to print that cvar 90 | /// 91 | NeverAsString = (1 << 12), 92 | 93 | /// 94 | /// It's a ConVar that's shared between the client and the server. 95 | /// At signon, the values of all such ConVars are sent from the server to the client= (skipped for local 96 | /// client, of course ) 97 | /// If a change is requested it must come from the console= (i.e., no remote client changes) 98 | /// If a value is changed while a server is active, it's replicated to all connected clients 99 | /// server setting enforced on clients, 100 | /// TODO rename to FCAR_SERVER at some time 101 | /// 102 | Replicated = (1 << 13), 103 | 104 | /// 105 | /// Only useable in singleplayer / debug / multiplayer & sv_cheats 106 | /// 107 | Cheat = (1 << 14), 108 | 109 | //causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated 110 | SplitScreen = (1 << 15), 111 | 112 | /// 113 | /// record this cvar when starting a demo file 114 | /// 115 | Demo = (1 << 16), 116 | 117 | /// 118 | /// don't record these command in demofiles 119 | /// 120 | DoNotRecord = (1 << 17), 121 | 122 | /// 123 | /// This is one of the "added" FCVAR_SS variables for the splitscreen players 124 | /// 125 | SplitScreenAdded = (1 << 18), 126 | 127 | /// 128 | /// Cvars tagged with this are the only cvars avaliable to customers 129 | /// 130 | Release = (1 << 19), 131 | 132 | /// 133 | /// If this cvar changes, it forces a material reload 134 | /// 135 | ReloadMaterials = (1 << 20), 136 | 137 | 138 | /// 139 | /// If this cvar changes, if forces a texture reload 140 | /// 141 | ReloadTextures = (1 << 21), 142 | 143 | /// 144 | /// cvar cannot be changed by a client that is connected to a server 145 | /// 146 | NotConnected = (1 << 22), 147 | 148 | /// 149 | /// Indicates this cvar is read from the material system thread 150 | /// 151 | MaterialSystemThread = (1 << 23), 152 | 153 | 154 | /// 155 | /// cvar written to config.cfg on the Xbox 156 | /// 157 | ArchiveGameConsole = (1 << 24), 158 | 159 | 160 | /// 161 | /// the server is allowed to execute this command on clients via ClientCommand/NET_StringCmd/CBaseClientState::ProcessStringCmd. 162 | /// 163 | ServerCanExecute = (1 << 28), 164 | 165 | /// 166 | /// If this is set, then the server is not allowed to query this cvar's value= (via IServerPluginHelpers::StartQueryCvarValue). 167 | /// 168 | ServerCanNotQuery = (1 << 29), 169 | 170 | /// 171 | /// IVEngineClient::ClientCmd is allowed to execute this command. 172 | /// Note: IVEngineClient::ClientCmd_Unrestricted can run any client command. 173 | /// 174 | ClientCommandCanExecute = (1 << 30), 175 | 176 | /// 177 | /// used as a debugging tool necessary to check material system thread convars 178 | /// 179 | AccessibleFromThreads = (1 << 25), 180 | 181 | 182 | MaterialThreadMask = (ReloadMaterials | ReloadTextures | MaterialSystemThread), 183 | 184 | } 185 | -------------------------------------------------------------------------------- /Sdk/Enums/GameEngineVersion.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Enums; 2 | 3 | public enum GameEngineVersion 4 | { 5 | Unknown = 0, 6 | Left4Dead, 7 | Left4Dead2, 8 | Insurgency, 9 | CounterStrike, 10 | CounterStrike2, 11 | } 12 | -------------------------------------------------------------------------------- /Sdk/Enums/GameEventHookType.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Enums; 2 | 3 | 4 | public enum GameEventHookType 5 | { 6 | /// 7 | /// Fire Hook 8 | /// 9 | Pre, 10 | 11 | /// 12 | /// Not Impl 13 | /// 14 | PostNoCopy, 15 | 16 | /// 17 | /// Fire Post Hook 18 | /// 19 | Post 20 | } 21 | -------------------------------------------------------------------------------- /Sdk/Enums/PathType.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Enums; 2 | 3 | public enum PathType 4 | { 5 | /// 6 | /// Non base 7 | /// 8 | None, 9 | /// 10 | /// Abs mod folder 11 | /// 12 | Game, 13 | /// 14 | /// SourceSharp绝对路径 15 | /// 16 | SourceSharpAbsolute, 17 | } 18 | -------------------------------------------------------------------------------- /Sdk/Enums/PlayerListenerType.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Enums; 2 | 3 | public enum PlayerListenerType 4 | { 5 | /// 6 | /// 是否允许建立链接 7 | /// 8 | ConnectHook, 9 | 10 | /// 11 | /// 建立连接时 12 | /// 13 | Connected, 14 | 15 | /// 16 | /// 加入游戏 17 | /// 18 | PutInServer, 19 | 20 | /// 21 | /// 验证Steam Ticket 22 | /// 23 | Authorized, 24 | 25 | /// 26 | /// 检查Steam且已经在游戏里 27 | /// 28 | PostAdminCheck, 29 | 30 | /// 31 | /// 尝试退出游戏时 32 | /// 33 | Disconnecting, 34 | 35 | /// 36 | /// 完全退出游戏 37 | /// 38 | Disconnected, 39 | 40 | /// 41 | /// 发送KV命令 42 | /// 43 | CommandKeyValues, 44 | 45 | /// 46 | /// 发送KV命令处理后 47 | /// 48 | CommandKeyValuesPost, 49 | 50 | /// 51 | /// 修改设置后 52 | /// 53 | SettingsChanged, 54 | } 55 | -------------------------------------------------------------------------------- /Sdk/Enums/PluginStatus.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Enums; 2 | 3 | public enum PluginStatus 4 | { 5 | None = 0, 6 | /// 7 | /// 已加载 8 | /// 9 | Checked, 10 | /// 11 | /// 运行中 12 | /// 13 | Running, 14 | /// 15 | /// 运行时发生错误 16 | /// 17 | Error, 18 | /// 19 | /// 发生错误无法运行 20 | /// 21 | Failed, 22 | } -------------------------------------------------------------------------------- /Sdk/Interfaces/IAdminManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Models; 2 | using System.Collections.Generic; 3 | 4 | namespace SourceSharp.Sdk.Interfaces; 5 | 6 | public interface IAdminManager : IRuntime 7 | { 8 | /// 9 | /// 创建管理员 10 | /// 11 | /// Steam 64位 ID 12 | /// 13 | /// AdminUser实例 14 | AdminUser CreateAdmin(ulong steamId, string name); 15 | 16 | /// 17 | /// 注销管理员 18 | /// 19 | /// AdminUser实例 20 | void RemoveAdmin(AdminUser admin); 21 | 22 | /// 23 | /// 获取所有管理员 24 | /// 25 | /// IReadOnlyList 26 | IReadOnlyList GetAdmins(); 27 | 28 | /// 29 | /// 通过SteamId搜索AdminUser实例 30 | /// 31 | /// Steam 64位 ID 32 | /// AdminUser实例 33 | AdminUser? FindAdminByIdentity(ulong steamId); 34 | } 35 | -------------------------------------------------------------------------------- /Sdk/Interfaces/IGameEngine.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Interfaces; 2 | 3 | // TODO FindMap 4 | 5 | public interface IGameEngine : IRuntime 6 | { 7 | /// 8 | /// 当前地图的运行时间 9 | /// 10 | /// 使用指针读取 11 | /// gpGlobals->curtime 或 缓存的值 12 | float GetGameTime(bool readFromPtr = false); 13 | 14 | /// 15 | /// 获取引擎时间 16 | /// 17 | /// Plat_Time 18 | float GetEngineTime(); 19 | 20 | /// 21 | /// 上一帧花费的时间 22 | /// 23 | /// engine time span 24 | float GetGameFrameTime(); 25 | 26 | /// 27 | /// 当前地图运行的帧数 28 | /// 29 | /// 使用指针读取 30 | /// gpGlobals->tickCount 或 缓存的值 31 | int GetGameTickCount(bool readFromPtr = false); 32 | 33 | /// 34 | /// 检查地图是否是有效的bsp地图 35 | /// 36 | /// 文件名 (不含.bsp) 37 | /// True = Valid 38 | bool IsMapValid(string map); 39 | 40 | /// 41 | /// 获取当前的地图 42 | /// 43 | /// 地图名, 不含.bsp 44 | string GetCurrentMap(); 45 | 46 | /// 47 | /// 预加载模型 48 | /// 49 | /// 路径 50 | /// 预加载 51 | /// Index in StringTable 52 | int PrecacheModel(string model, bool preLoad = false); 53 | 54 | /// 55 | /// 预加载Decal 56 | /// 57 | /// 路径 58 | /// 预加载 59 | /// Index in StringTable 60 | int PrecacheDecal(string decal, bool preLoad = false); 61 | 62 | /// 63 | /// 预加载Generic 64 | /// 65 | /// 路径 66 | /// 预加载 67 | /// Index in StringTable 68 | int PrecacheGeneric(string generic, bool preLoad = false); 69 | 70 | /// 71 | /// 预加载音频 72 | /// 73 | /// 路径 74 | /// 预加载 75 | /// Index in StringTable 76 | bool PrecacheSound(string sound, bool preLoad = false); 77 | 78 | /// 79 | /// 模型是否已被预加载 80 | /// 81 | /// 路径 82 | bool IsModelPreCached(string model); 83 | 84 | /// 85 | /// Decal是否已被已加载 86 | /// 87 | /// 路径 88 | bool IsDecalPreCached(string decal); 89 | 90 | /// 91 | /// Generic是否已被预加载 92 | /// 93 | /// 路径 94 | bool IsGenericPreCached(string generic); 95 | 96 | /// 97 | /// 更换地图 98 | /// 99 | /// 地图名 100 | /// 更换的原因 101 | bool ChangeLevel(string map, string reason); 102 | } 103 | -------------------------------------------------------------------------------- /Sdk/Interfaces/IPlayerManager.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Models; 2 | using System.Collections.Generic; 3 | 4 | namespace SourceSharp.Sdk.Interfaces; 5 | 6 | public interface IPlayerManager : IRuntime 7 | { 8 | /// 9 | /// 获取GamePlayer实例 10 | /// 11 | /// Client Index 12 | /// GamePlayer实例 13 | GamePlayer? GetGamePlayer(int index); 14 | 15 | /// 16 | /// 获取GamePlayer实例 17 | /// 18 | /// Client Serial 19 | /// GamePlayer实例 20 | GamePlayer? GetGamePlayer(uint serial); 21 | 22 | /// 23 | /// 获取GamePlayer实例 24 | /// 25 | /// userId 26 | /// Client Index 27 | GamePlayer? GetGamePlayerByUserId(int userId); 28 | 29 | /// 30 | /// 获取玩家列表 31 | /// (不包含SourceTV, Replay) 32 | /// 33 | /// GamePlayer实例列表 34 | IReadOnlyList GetPlayers(); 35 | 36 | /// 37 | /// 获取玩家列表 38 | /// (Player, FakeClient, Replay, SourceTV) 39 | /// 40 | /// GamePlayer实例列表 41 | IReadOnlyList GetClients(); 42 | 43 | /// 44 | /// 获取游戏内允许的最大人数 45 | /// 46 | /// 人数 47 | uint GetMaxClients(); 48 | 49 | /// 50 | /// 获取当前已建立连接的人数 51 | /// 52 | /// 人数 53 | uint GetNumPlayers(); 54 | 55 | /// 56 | /// 刷新全体玩家的管理员数据 57 | /// 58 | void RunAdminChecks(); 59 | } 60 | -------------------------------------------------------------------------------- /Sdk/Interfaces/IPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Interfaces; 4 | 5 | public interface IPlugin 6 | { 7 | /// 8 | /// 初始化加载... 9 | /// 10 | /// True = 成功 11 | bool OnLoad(); 12 | 13 | /// 14 | /// 在所有Plugin加载完成后调用 15 | /// 16 | void OnAllLoad(); 17 | 18 | /// 19 | /// 查询当前插件是否可用 20 | /// 21 | /// False = 插件失败 22 | bool QueryRunning(); 23 | 24 | /// 25 | /// 卸载时调用 26 | /// 27 | void OnShutdown(); 28 | 29 | /// 30 | /// Interface被卸载时调用 31 | /// 32 | /// interface 实例 33 | void NotifyInterfaceDrop(IRuntime @interface); 34 | 35 | /// 36 | /// 获取插件唯一标识符 (Once load) 37 | /// 38 | Guid GetUniqueId(); 39 | } 40 | 41 | public abstract class PluginBase : IPlugin 42 | { 43 | #nullable disable 44 | protected readonly ISourceSharp _sourceSharp; 45 | protected readonly IShareSystem _shareSystem; 46 | #nullable restore 47 | 48 | public virtual bool OnLoad() 49 | { 50 | return true; 51 | } 52 | 53 | public virtual void OnAllLoad() 54 | { 55 | 56 | } 57 | 58 | public virtual void OnShutdown() 59 | { 60 | 61 | } 62 | 63 | public virtual bool QueryRunning() 64 | { 65 | return true; 66 | } 67 | 68 | public virtual void NotifyInterfaceDrop(IRuntime @interface) 69 | { 70 | 71 | } 72 | 73 | private readonly Guid _uniqueId = Guid.NewGuid(); 74 | public Guid GetUniqueId() => _uniqueId; 75 | } -------------------------------------------------------------------------------- /Sdk/Interfaces/IRuntime.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Interfaces; 2 | 3 | public interface IRuntime 4 | { 5 | /// 6 | /// 获取Interface版本 7 | /// 8 | /// 版本 9 | uint GetInterfaceVersion(); 10 | 11 | /// 12 | /// 获取Interface名称 13 | /// 14 | /// 名称 15 | string GetInterfaceName(); 16 | } 17 | -------------------------------------------------------------------------------- /Sdk/Interfaces/IShareSystem.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Interfaces; 2 | 3 | public interface IShareSystem 4 | { 5 | /// 6 | /// 注册Interface到依赖注入Scope 7 | /// 8 | /// IRuntime实现 9 | /// Plugin实例 10 | void AddInterface(IRuntime @interface, IPlugin @plugin); 11 | 12 | /// 13 | /// 获取必须的Interface, 不为空 14 | /// 15 | /// IRuntime实现的Interface 16 | /// IRuntime实现 17 | T GetRequiredInterface(uint version) where T : class, IRuntime; 18 | 19 | /// 20 | /// 获取可选的Interface, 可空 21 | /// 22 | /// IRuntime实现的Interface 23 | /// IRuntime实现 24 | T? GetInterface(uint version) where T : class, IRuntime; 25 | } 26 | -------------------------------------------------------------------------------- /Sdk/Interfaces/ISourceSharp.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using SourceSharp.Sdk.Models; 3 | using System; 4 | 5 | namespace SourceSharp.Sdk.Interfaces; 6 | 7 | public interface ISourceSharp : IRuntime 8 | { 9 | /// 10 | /// 获取游戏Mod位置 11 | /// e.g. csgo/insurgency 12 | /// 13 | /// Abs路径 14 | string GetGamePath(); 15 | 16 | /// 17 | /// 获取SRCDS根目录路径 18 | /// 19 | /// Abs路径 20 | string GetRootPath(); 21 | 22 | /// 23 | /// Path.Combine封装, 可以快速格式化路径 24 | /// 25 | /// 路径类型 26 | /// 参数 27 | /// Abs路径 28 | string BuildPath(PathType type, params string[] format); 29 | 30 | /// 31 | /// 打印到控制台 32 | /// 33 | /// 内容 34 | void PrintLine(string message); 35 | 36 | /// 37 | /// 输出到日志文件 (Async) 38 | /// 39 | /// 内容 40 | void LogMessage(string message); 41 | 42 | /// 43 | /// 输出到错误日志 (Async) 44 | /// 45 | /// 内容 46 | void LogError(string message); 47 | 48 | /// 49 | /// 在主线程调用Action, 50 | /// 当前线程为主线程时立即调用, 51 | /// 否则将在下一帧开始时调用. 52 | /// 53 | /// action method 54 | void Invoke(Action action); 55 | 56 | /// 57 | /// 在下一帧开始时由主线程调用 58 | /// 59 | /// action method 60 | void InvokeNextFrame(Action action); 61 | 62 | /// 63 | /// 获取最大玩家人数 64 | /// 65 | /// 最大玩家人数 66 | int GetMaxClients(); 67 | 68 | /// 69 | /// 获取最大真实人类玩家数 70 | /// 71 | /// 72 | int GetMaxHumanPlayers(); 73 | 74 | /// 75 | /// 获取当前的游戏 76 | /// 77 | /// EngineVersion 78 | GameEngineVersion GetEngineVersion(); 79 | 80 | /// 81 | /// 执行服务端命令 82 | /// 83 | void ExecuteServerCommand(string command); 84 | 85 | /// 86 | /// 添加命令到服务器命令缓冲池 87 | /// 88 | /// 89 | void InsertServerCommand(string command); 90 | 91 | /// 92 | /// 执行并清理服务器命令缓冲池 93 | /// 94 | void ServerExecute(); 95 | 96 | /// 97 | /// 查找ConVar 98 | /// 99 | /// ConVar名 100 | /// ConVar实例 101 | ConVar? FindConVar(string name); 102 | 103 | /// 104 | /// 创建游戏事件. 105 | /// 该事件如果没有调用Fire, 106 | /// 则必须要使用Cancel进行关闭. 107 | /// 108 | /// 事件名 109 | /// 设置默认发送值 110 | /// GameEvent实例 111 | GameEvent? CreateEvent(string name, bool broadcast); 112 | } 113 | -------------------------------------------------------------------------------- /Sdk/Models/AdminUser.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | 3 | namespace SourceSharp.Sdk.Models; 4 | 5 | public abstract class AdminUser 6 | { 7 | /// 8 | /// 管理员Id 9 | /// 10 | public uint Id => GetId(); 11 | 12 | /// 13 | /// 管理员名字 14 | /// 15 | public string Name => GetName(); 16 | 17 | /// 18 | /// 管理员SteamId 19 | /// 20 | public ulong SteamId => GetSteamId(); 21 | 22 | /// 23 | /// 管理员AdminFlags 24 | /// 25 | public AdminFlags Flags => GetFlags(); 26 | 27 | /// 28 | /// 覆盖Flags 29 | /// 30 | /// 新的Flags 31 | public abstract void SetAdminFlags(AdminFlags flags); 32 | 33 | /// 34 | /// 新增Access Flags 35 | /// 36 | /// 要增加的Flags 37 | public abstract void AddAdminFlags(AdminFlags flags); 38 | 39 | /// 40 | /// 清除Access Flags 41 | /// 42 | /// 要删除的Flags 43 | public abstract void RemoveAdminFlags(AdminFlags flags); 44 | 45 | /// 46 | /// 检查Access Flags 47 | /// 48 | /// 要检查的Flags 49 | /// 拥有指定的Flags 50 | public abstract bool HasFlags(AdminFlags flags); 51 | 52 | #region internal implements 53 | 54 | protected abstract uint GetId(); 55 | protected abstract string GetName(); 56 | protected abstract ulong GetSteamId(); 57 | protected abstract AdminFlags GetFlags(); 58 | 59 | #endregion 60 | } -------------------------------------------------------------------------------- /Sdk/Models/ConVar.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using SourceSharp.Sdk.Interfaces; 3 | using SourceSharp.Sdk.Structs; 4 | using System; 5 | 6 | namespace SourceSharp.Sdk.Models; 7 | 8 | public abstract class ConVar 9 | { 10 | /// 11 | /// 最大值/最小值 12 | /// 13 | public ConVarBounds Bounds 14 | { 15 | get => GetBounds(); 16 | set => SetBounds(value); 17 | } 18 | 19 | /// 20 | /// Flags 21 | /// 22 | public ConVarFlags Flags => GetFlags(); 23 | 24 | /// 25 | /// 新增Flags 26 | /// 27 | /// flags 28 | public abstract void AddFlags(ConVarFlags flags); 29 | 30 | /// 31 | /// 名字 32 | /// 33 | public string Name => GetName(); 34 | 35 | /// 36 | /// 默认值 37 | /// 38 | public string DefaultValue => GetDefaultValue(); 39 | 40 | /// 41 | /// 说明文字 42 | /// 43 | public string Description => GetDescription(); 44 | 45 | /// 46 | /// 读取指定类型的值 47 | /// 48 | /// bool, int, float, string 49 | /// 50 | public abstract T Get() where T : IConvertible; 51 | 52 | /// 53 | /// 设置指定类型的值 54 | /// 55 | /// bool, int, float, string 56 | /// 57 | public abstract void Set(T value) where T : IConvertible; 58 | 59 | /// 60 | /// 不修改实际值的情况下, 修改客户端中的值 61 | /// 62 | /// 玩家 63 | /// True = 成功 64 | public abstract bool ReplicateToPlayers(GamePlayer[] players); 65 | 66 | /// 67 | /// 订阅修改事件 68 | /// 69 | public abstract void RegisterChangeHook(IPlugin caller, Action callback); 70 | 71 | protected abstract ConVarBounds GetBounds(); 72 | protected abstract void SetBounds(ConVarBounds bounds); 73 | protected abstract string GetDescription(); 74 | protected abstract string GetDefaultValue(); 75 | protected abstract ConVarFlags GetFlags(); 76 | protected abstract string GetName(); 77 | } 78 | -------------------------------------------------------------------------------- /Sdk/Models/ConsoleCommand.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Models; 2 | 3 | public sealed class ConsoleCommand 4 | { 5 | public string[] Args { get; } 6 | public string ArgString { get; } 7 | public int ArgC { get; } 8 | 9 | public string Command => Args[0].ToLower(); 10 | 11 | public ConsoleCommand(string argString, string[] args, int argC) 12 | { 13 | ArgString = argString; 14 | Args = args; 15 | ArgC = argC; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Sdk/Models/GameEntity.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Models; 2 | 3 | public abstract class GameEntity 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /Sdk/Models/GameEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SourceSharp.Sdk.Models; 4 | 5 | // TODO CreateNew, Fire, FireToPlayers, Cancel 6 | 7 | public abstract class GameEvent 8 | { 9 | /// 10 | /// Event 名字 11 | /// 12 | public string Name => GetName(); 13 | 14 | public bool Broadcast => GetBroadcast(); 15 | 16 | /// 17 | /// 读取Event的值 18 | /// 19 | /// bool, int, float, string 20 | /// 字段名 21 | /// 22 | public abstract T Get(string key) where T : IConvertible; 23 | 24 | /// 25 | /// 设置Event的值 26 | /// 27 | /// bool, int, float, string 28 | /// 字段名 29 | /// 30 | /// True = 成功 31 | public abstract bool Set(string key, T value) where T : IConvertible; 32 | 33 | /// 34 | /// Free Event 35 | /// 36 | public abstract void Cancel(); 37 | 38 | protected abstract string GetName(); 39 | protected abstract bool SetBroadcast(bool broadcast); 40 | protected abstract bool GetBroadcast(); 41 | } 42 | -------------------------------------------------------------------------------- /Sdk/Models/GamePlayer.cs: -------------------------------------------------------------------------------- 1 | using SourceSharp.Sdk.Enums; 2 | using System; 3 | using System.Net; 4 | using System.Text.Json; 5 | 6 | namespace SourceSharp.Sdk.Models; 7 | 8 | // TODO IPlayerInfo, INetChannel 9 | // TODO CanTarget, IsAlive, IsObserver, IsTimingOut, GetInfo, GetTeam, ChangeTeam 10 | // TODO AbsOrigin, AbsAngles ... 11 | // TODO ExecuteCommand, ExecuteFakeCommand, ExecuteFakeCommandKeyValues 12 | 13 | public abstract class GamePlayer 14 | { 15 | /// 16 | /// 玩家名称 17 | /// 18 | public string Name => GetName(); 19 | 20 | /// 21 | /// IP Address 22 | /// 23 | public IPEndPoint RemoteEndPoint => GetRemoteEndPoint(); 24 | 25 | /// 26 | /// SteamId - 64 27 | /// 28 | public ulong SteamId => GetSteamId(); 29 | 30 | /// 31 | /// 管理员权限 32 | /// 33 | public AdminFlags AdminFlags => GetAdminFlags(); 34 | 35 | /// 36 | /// 是否是管理员 37 | /// 38 | public bool IsAdmin => GetAdminFlags() != AdminFlags.None; 39 | 40 | /// 41 | /// 获取有效的SteamId 42 | /// 43 | public ulong ValidateSteamId 44 | { 45 | get 46 | { 47 | if (!GetIsAuthorized()) 48 | { 49 | throw new InvalidOperationException("GamePlayer is unauthorized!"); 50 | } 51 | return SteamId; 52 | } 53 | } 54 | 55 | /// 56 | /// 判断该玩家是否已经离开游戏 57 | /// 该功能常用于异步读取数据时的判断 58 | /// 59 | public bool IsValid => !GetIsDisconnected(); 60 | 61 | /// 62 | /// UserId of engine 63 | /// 64 | public int UserId => GetUserId(); 65 | 66 | /// 67 | /// Serial number 68 | /// 69 | public uint Serial => GetSerial(); 70 | 71 | /// 72 | /// Entity index 73 | /// 74 | public int Index => GetIndex(); 75 | 76 | /// 77 | /// 玩家是否在游戏中 78 | /// 79 | public bool IsInGame => GetIsInGame(); 80 | 81 | /// 82 | /// 是否是FakeClient 83 | /// 84 | public bool IsFakeClient => GetIsFakeClient(); 85 | 86 | /// 87 | /// 是否是SourceTV/GOTV 88 | /// 89 | public bool IsSourceTv => GetIsSourceTv(); 90 | 91 | /// 92 | /// 是否是Replay 93 | /// 94 | public bool IsReplay => GetIsReplay(); 95 | 96 | /// 97 | /// 是否已完成Steam Validation 98 | /// 99 | public bool IsAuthorized => GetIsAuthorized(); 100 | 101 | /// 102 | /// 正在退出游戏! 103 | /// 104 | public bool IsDisconnecting => GetIsDisconnecting(); 105 | 106 | /// 107 | /// 打印到控制台 108 | /// 109 | /// 内容 110 | public abstract void Print(string message); 111 | 112 | /// 113 | /// 发送TextMsg 114 | /// 115 | /// 内容 116 | public abstract void TextMsg(string message); 117 | 118 | /// 119 | /// 踢出游戏 120 | /// 121 | public abstract void Kick(string message); 122 | 123 | #region override 124 | 125 | private readonly Guid _uniqueId = Guid.NewGuid(); 126 | 127 | public override int GetHashCode() 128 | => _uniqueId.GetHashCode(); 129 | 130 | public override bool Equals(object? obj) 131 | { 132 | if (obj is GamePlayer p2) 133 | { 134 | return _uniqueId == p2._uniqueId; 135 | } 136 | 137 | return false; 138 | } 139 | 140 | public override string ToString() => JsonSerializer.Serialize(this, new JsonSerializerOptions 141 | { 142 | WriteIndented = true, 143 | IncludeFields = false, 144 | }); 145 | 146 | #endregion 147 | 148 | #region internal implements 149 | 150 | protected abstract ulong GetSteamId(); 151 | protected abstract uint GetSerial(); 152 | protected abstract int GetUserId(); 153 | protected abstract int GetIndex(); 154 | protected abstract string GetName(); 155 | protected abstract IPEndPoint GetRemoteEndPoint(); 156 | protected abstract AdminFlags GetAdminFlags(); 157 | 158 | protected abstract bool GetIsDisconnecting(); 159 | protected abstract bool GetIsDisconnected(); 160 | protected abstract bool GetIsInGame(); 161 | protected abstract bool GetIsAuthorized(); 162 | protected abstract bool GetIsFakeClient(); 163 | protected abstract bool GetIsSourceTv(); 164 | protected abstract bool GetIsReplay(); 165 | #endregion 166 | } 167 | -------------------------------------------------------------------------------- /Sdk/Sdk.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | disable 5 | 11 6 | enable 7 | SourceSharp.Sdk 8 | SourceSharp.Sdk 9 | 1.0 10 | 1 11 | $(VersionPrefix).$(VersionSuffix) 12 | https://github.com/SourceSharp 13 | ©2023 Kyle, Bone 14 | https://github.com/SourceSharp/runtime 15 | 16 | 17 | True 18 | x86 19 | 20 | 21 | True 22 | x64 23 | 24 | 25 | True 26 | x86 27 | 28 | 29 | True 30 | x64 31 | 32 | -------------------------------------------------------------------------------- /Sdk/SharedDefines.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk; 2 | 3 | public static class SharedDefines 4 | { 5 | public const string InterfacePrefix = "ISOURCESHARP_"; 6 | 7 | public const string CoreInterfaceName = "ISOURCESHARP_CORE"; 8 | public const string GameEventListenerInterfaceName = "ISOURCESHARP_GAMEEVENTLISTENER"; 9 | 10 | public const uint GameEventListenerInterfaceVersion = 1; 11 | 12 | public const string CommandListenerInterfaceName = "ISOURCESHARP_COMMANDLISTENER"; 13 | public const uint CommandListenerInterfaceVersion = 1; 14 | 15 | public const string PlayerListenerInterfaceName = "ISOURCESHARP_PLAYERLISTENER"; 16 | public const uint PlayerListenerInterfaceVersion = 1; 17 | 18 | public const string PlayerManagerInterfaceName = "ISOURCESHARP_PLAYERMANAGER"; 19 | public const uint PlayerManagerInterfaceVersion = 1; 20 | 21 | public const string AdminManagerInterfaceName = "ISOURCESHARP_ADMINMANAGER"; 22 | public const uint AdminManagerInterfaceVersion = 1; 23 | 24 | public const string ConVarManagerInterfaceName = "ISOURCESHARP_CONVARMANAGER"; 25 | public const uint ConVarManagerInterfaceVersion = 1; 26 | } 27 | -------------------------------------------------------------------------------- /Sdk/Structs/ActionResponse.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Structs; 2 | 3 | public readonly struct ActionResponse 4 | { 5 | public T Response { get; } 6 | 7 | public int Code { get; } 8 | 9 | public ActionResponse(int code, T response) 10 | { 11 | Code = code; 12 | Response = response; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sdk/Structs/ConVarBounds.cs: -------------------------------------------------------------------------------- 1 | namespace SourceSharp.Sdk.Structs; 2 | 3 | public readonly struct ConVarBounds 4 | { 5 | public float Min { get; } = 0.0f; 6 | public float Max { get; } = 0.0f; 7 | 8 | public bool HasMin { get; } = false; 9 | 10 | public bool HasMax { get; } = false; 11 | 12 | public ConVarBounds(float min, float max, bool hasMin, bool hasMax) 13 | { 14 | Min = min; 15 | Max = max; 16 | HasMin = hasMin; 17 | HasMax = hasMax; 18 | } 19 | } 20 | --------------------------------------------------------------------------------