├── .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 |
--------------------------------------------------------------------------------