├── .gitattributes
├── .github
└── workflows
│ └── build-publish.yaml
├── .gitignore
├── Directory.Build.props
├── FiveMMsgPack
└── MsgPack.dll
├── FxEvents.sln
├── GitVersion.yml
├── LICENSE.txt
├── README.md
├── build
└── Version.props
├── dependencies
└── RedM
│ ├── CitizenFX.Core.dll
│ └── CitizenFX.Core.xml
├── scripts
└── .gitkeep
└── src
├── FxEvents.Client
├── .editorconfig
├── EventDispatcher.cs
├── EventHub.cs
├── EventSystem
│ └── ClientGateway.cs
└── FxEvents.Client.csproj
├── FxEvents.Server
├── .editorconfig
├── EventDispatcher.cs
├── EventHub.cs
├── EventSystem
│ └── ServerGateway.cs
└── FxEvents.Server.csproj
└── FxEvents.Shared
├── BinaryHelper.cs
├── ContractResolvers.cs
├── Encryption
├── Curve25519.cs
├── Curve25519Inner.cs
└── Encryption.cs
├── EventSubsystem
├── Attributes
│ ├── ForceAttribute.cs
│ ├── FxEventAttribute.cs
│ └── IgnoreAttribute.cs
├── BaseGateway.cs
├── BaseGatewayHelpers.cs
├── Diagnostics
│ ├── IEventLogger.cs
│ ├── Impl
│ │ ├── ClientStopwatch.cs
│ │ └── ServerStopwatch.cs
│ └── StopwatchUtil.cs
├── EventsDictionary.cs
├── Exceptions
│ └── EventException.cs
├── ISerializable.cs
├── ISource.cs
├── Message
│ ├── EventFlowType.cs
│ ├── EventMessage.cs
│ ├── EventParameter.cs
│ ├── EventRemote.cs
│ └── EventResponseMessage.cs
├── Models
│ ├── EventObservable.cs
│ └── EventValueHolder.cs
├── Serialization
│ ├── ISerialization.cs
│ ├── Implementations
│ │ ├── BinarySerialization.cs
│ │ ├── JsonSerialization.cs
│ │ ├── MsgPack
│ │ │ └── MsgPackResolvers
│ │ │ │ ├── DoubleFixer.cs
│ │ │ │ ├── EntityResolver.cs
│ │ │ │ ├── ISourceResolver.cs
│ │ │ │ ├── KeyValuePairResolver.cs
│ │ │ │ ├── Matrix3x3Resolver.cs
│ │ │ │ ├── MatrixResolver.cs
│ │ │ │ ├── PlayerResolver.cs
│ │ │ │ ├── QuaternionResolver.cs
│ │ │ │ ├── SnowflakeResolver.cs
│ │ │ │ ├── TupleResolver.cs
│ │ │ │ ├── ValueTupleResolver.cs
│ │ │ │ └── VectorResolver.cs
│ │ └── MsgPackSerialization.cs
│ ├── MsgPackCode.cs
│ ├── SerializationContext.cs
│ ├── SerializationException.cs
│ └── TypeConvert.cs
├── ServerId.cs
├── TamperType.cs
└── TypeConverter.cs
├── FxEvents.Shared.projitems
├── FxEvents.Shared.shproj
├── JsonHelper.cs
├── Logger
├── ILogger.cs
├── Logger.cs
└── LoggerColors.cs
├── Snowflake
├── Clock.cs
├── Program.cs
├── Serialization
│ └── SnowflakeConverter.cs
├── Snowflake.cs
├── SnowflakeConfiguration.cs
├── SnowflakeFragments.cs
├── SnowflakeGenerator.cs
└── SnowflakeRepresentation.cs
├── TypeConverter.cs
└── TypeExtensions
├── Base64Extensions.cs
├── BooleanExtensions.cs
├── DelegateExtensions.cs
├── EnumerableExtensions.cs
├── KeyValuePairExtensions.cs
├── ObjectExtensions.cs
├── StringExtensions.cs
├── TaskExtensions.cs
├── TypeCache.cs
└── ValueTuple.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/workflows/build-publish.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | tags:
4 | - "v[0-9]+.[0-9]+"
5 | - "v[0-9]+.[0-9]+.[0-9]+"
6 | - "v[0-9]+.[0-9]+.[0-9]+.[0-9]+"
7 | if: startsWith(github.ref, 'refs/tags/')
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.ref_name }}
11 | cancel-in-progress: true
12 |
13 | env:
14 | is-tag: ${{ startsWith(github.ref, 'refs/tags/v') }}
15 |
16 | jobs:
17 | build:
18 | runs-on: windows-latest
19 | strategy:
20 | matrix:
21 | client: ['FiveM', 'RedM']
22 | steps:
23 | - name: Checkout repository
24 | uses: actions/checkout@v4
25 | with:
26 | fetch-depth: 0
27 |
28 | - name: Setup - GitVersion
29 | uses: gittools/actions/gitversion/setup@v0
30 | with:
31 | versionSpec: '5.x'
32 | preferLatestVersion: true
33 |
34 | - name: Setup - dotnet framework 4.8
35 | run: |
36 | choco install netfx-4.8-devpack -y
37 | dotnet --version # Verify installation
38 | - name: Setup - dotnet 8
39 | uses: actions/setup-dotnet@v4
40 | with:
41 | source-url: https://nuget.pkg.github.com/${{ github.repository_owner }}/index.json
42 | env:
43 | NUGET_AUTH_TOKEN: ${{ github.token }}
44 |
45 | - name: GitVersion
46 | id: git-version
47 | uses: gittools/actions/gitversion/execute@v0
48 | with:
49 | useConfigFile: true
50 |
51 | - name: Install dotnet tool
52 | run: dotnet tool install -g dotnetCampus.TagToVersion
53 |
54 | - name: Set tag to version
55 | run: dotnet TagToVersion -t ${{ github.ref }}
56 |
57 | - name: Dotnet - Restore
58 | run: dotnet restore
59 |
60 | - name: Dotnet - Build
61 | run: dotnet build -c "Release ${{ matrix.client }}" --no-restore
62 |
63 | - name: Dotnet - Test
64 | run: dotnet test -c "Release ${{ matrix.client }}" --no-restore
65 |
66 | - name: Dotnet - Pack
67 | run: dotnet pack -c "Release ${{ matrix.client }}" --no-restore
68 |
69 | - name: Publish - NuGet
70 | if: ${{ env.is-tag == 'true' }}
71 | run: dotnet nuget push **/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} --skip-duplicate
72 | working-directory: CompiledLibs
73 |
74 | - name: Publish - GitHub
75 | if: ${{ env.is-tag == 'true' }}
76 | run: dotnet nuget push **/*.nupkg --skip-duplicate
77 | working-directory: CompiledLibs
78 |
--------------------------------------------------------------------------------
/.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/master/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 | obj/
33 | Obj/
34 | [Oo]ut/
35 | [Ll]og/
36 | [Ll]ogs/
37 |
38 | # Visual Studio 2015/2017 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | #wwwroot/
42 |
43 | # Visual Studio 2017 auto generated files
44 | Generated\ Files/
45 |
46 | # MSTest test Results
47 | [Tt]est[Rr]esult*/
48 | [Bb]uild[Ll]og.*
49 |
50 | # NUnit
51 | *.VisualState.xml
52 | TestResult.xml
53 | nunit-*.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # Benchmark Results
61 | BenchmarkDotNet.Artifacts/
62 |
63 | # .NET Core
64 | project.lock.json
65 | project.fragment.lock.json
66 | artifacts/
67 |
68 | # ASP.NET Scaffolding
69 | ScaffoldingReadMe.txt
70 |
71 | # StyleCop
72 | StyleCopReport.xml
73 |
74 | # Files built by Visual Studio
75 | *_i.c
76 | *_p.c
77 | *_h.h
78 | *.ilk
79 | *.meta
80 | *.obj
81 | *.iobj
82 | *.pch
83 | *.pdb
84 | *.ipdb
85 | *.pgc
86 | *.pgd
87 | *.rsp
88 | *.sbr
89 | *.tlb
90 | *.tli
91 | *.tlh
92 | *.tmp
93 | *.tmp_proj
94 | *_wpftmp.csproj
95 | *.log
96 | *.vspscc
97 | *.vssscc
98 | .builds
99 | *.pidb
100 | *.svclog
101 | *.scc
102 |
103 | # Chutzpah Test files
104 | _Chutzpah*
105 |
106 | # Visual C++ cache files
107 | ipch/
108 | *.aps
109 | *.ncb
110 | *.opendb
111 | *.opensdf
112 | *.sdf
113 | *.cachefile
114 | *.VC.db
115 | *.VC.VC.opendb
116 |
117 | # Visual Studio profiler
118 | *.psess
119 | *.vsp
120 | *.vspx
121 | *.sap
122 |
123 | # Visual Studio Trace Files
124 | *.e2e
125 |
126 | # TFS 2012 Local Workspace
127 | $tf/
128 |
129 | # Guidance Automation Toolkit
130 | *.gpState
131 |
132 | # ReSharper is a .NET coding add-in
133 | _ReSharper*/
134 | *.[Rr]e[Ss]harper
135 | *.DotSettings.user
136 |
137 | # TeamCity is a build add-in
138 | _TeamCity*
139 |
140 | # DotCover is a Code Coverage Tool
141 | *.dotCover
142 |
143 | # AxoCover is a Code Coverage Tool
144 | .axoCover/*
145 | !.axoCover/settings.json
146 |
147 | # Coverlet is a free, cross platform Code Coverage Tool
148 | coverage*.json
149 | coverage*.xml
150 | coverage*.info
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio LightSwitch build output
300 | **/*.HTMLClient/GeneratedArtifacts
301 | **/*.DesktopClient/GeneratedArtifacts
302 | **/*.DesktopClient/ModelManifest.xml
303 | **/*.Server/GeneratedArtifacts
304 | **/*.Server/ModelManifest.xml
305 | _Pvt_Extensions
306 |
307 | # Paket dependency manager
308 | .paket/paket.exe
309 | paket-files/
310 |
311 | # FAKE - F# Make
312 | .fake/
313 |
314 | # CodeRush personal settings
315 | .cr/personal
316 |
317 | # Python Tools for Visual Studio (PTVS)
318 | __pycache__/
319 | *.pyc
320 |
321 | # Cake - Uncomment if you are using it
322 | # tools/**
323 | # !tools/packages.config
324 |
325 | # Tabs Studio
326 | *.tss
327 |
328 | # Telerik's JustMock configuration file
329 | *.jmconfig
330 |
331 | # BizTalk build output
332 | *.btp.cs
333 | *.btm.cs
334 | *.odx.cs
335 | *.xsd.cs
336 |
337 | # OpenCover UI analysis results
338 | OpenCover/
339 |
340 | # Azure Stream Analytics local run output
341 | ASALocalRun/
342 |
343 | # MSBuild Binary and Structured Log
344 | *.binlog
345 |
346 | # NVidia Nsight GPU debugger configuration file
347 | *.nvuser
348 |
349 | # MFractors (Xamarin productivity tool) working folder
350 | .mfractor/
351 |
352 | # Local History for Visual Studio
353 | .localhistory/
354 |
355 | # BeatPulse healthcheck temp database
356 | healthchecksdb
357 |
358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
359 | MigrationBackup/
360 |
361 | # scripts folder
362 | .scripts/
363 | *.bat
364 | *.log
365 |
366 | *.md
367 |
368 | # Ionide (cross platform F# VS Code tools) working folder
369 | .ionide/
370 |
371 | # Fody - auto-generated XML schema
372 | FodyWeavers.xsd
373 |
374 | # Ignored folders from New configuration
375 | [Rr]elease [Rr]ed[Mm]/
376 | [Rr]elease [Ff]ive[Mm]/
377 | [Dd]ebug [Rr]ed[Mm]/
378 | [Dd]ebug [Ff]ive[Mm]/
379 |
380 | # .idea files - Rider / JetBrains IDE's
381 | .idea
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/FiveMMsgPack/MsgPack.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manups4e/FxEvents/27c5d2647b769a16c4a9c38391bae150e79bc757/FiveMMsgPack/MsgPack.dll
--------------------------------------------------------------------------------
/FxEvents.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.3.32825.248
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FxEvents.Client", "src\FxEvents.Client\FxEvents.Client.csproj", "{4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FxEvents.Server", "src\FxEvents.Server\FxEvents.Server.csproj", "{5C4FECB5-C970-46AA-8536-E6DD716BDCE0}"
9 | EndProject
10 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "FxEvents.Shared", "src\FxEvents.Shared\FxEvents.Shared.shproj", "{7D81B737-B66A-4961-BFA0-6A69347EED78}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug FiveM|Any CPU = Debug FiveM|Any CPU
15 | Debug RedM|Any CPU = Debug RedM|Any CPU
16 | Release FiveM|Any CPU = Release FiveM|Any CPU
17 | Release RedM|Any CPU = Release RedM|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Debug FiveM|Any CPU.ActiveCfg = Debug FiveM|Any CPU
21 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Debug FiveM|Any CPU.Build.0 = Debug FiveM|Any CPU
22 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Debug RedM|Any CPU.ActiveCfg = Debug RedM|Any CPU
23 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Debug RedM|Any CPU.Build.0 = Debug RedM|Any CPU
24 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Release FiveM|Any CPU.ActiveCfg = Release FiveM|Any CPU
25 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Release FiveM|Any CPU.Build.0 = Release FiveM|Any CPU
26 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Release RedM|Any CPU.ActiveCfg = Release RedM|Any CPU
27 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}.Release RedM|Any CPU.Build.0 = Release RedM|Any CPU
28 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Debug FiveM|Any CPU.ActiveCfg = Debug|Any CPU
29 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Debug FiveM|Any CPU.Build.0 = Debug|Any CPU
30 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Debug RedM|Any CPU.ActiveCfg = Debug|Any CPU
31 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Debug RedM|Any CPU.Build.0 = Debug|Any CPU
32 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Release FiveM|Any CPU.ActiveCfg = Release|Any CPU
33 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Release FiveM|Any CPU.Build.0 = Release|Any CPU
34 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Release RedM|Any CPU.ActiveCfg = Release|Any CPU
35 | {5C4FECB5-C970-46AA-8536-E6DD716BDCE0}.Release RedM|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {46F9C3B8-35E3-4A06-8775-EF96C3B56EA8}
42 | EndGlobalSection
43 | GlobalSection(SharedMSBuildProjectFiles) = preSolution
44 | src\FxEvents.Shared\FxEvents.Shared.projitems*{4f57e7a2-97b7-499b-8f9a-57f0af74c9ac}*SharedItemsImports = 5
45 | src\FxEvents.Shared\FxEvents.Shared.projitems*{5c4fecb5-c970-46aa-8536-e6dd716bdce0}*SharedItemsImports = 5
46 | src\FxEvents.Shared\FxEvents.Shared.projitems*{7d81b737-b66a-4961-bfa0-6a69347eed78}*SharedItemsImports = 13
47 | EndGlobalSection
48 | EndGlobal
49 |
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: ContinuousDelivery
2 | branches:
3 | master:
4 | mode: ContinuousDeployment
5 | tag: ci
6 | track-merge-target: true
7 | ignore:
8 | sha: []
9 | merge-message-formats: {}
10 | tag-prefix: ""
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FxEvents: An Advanced Event Subsystem for FiveM using mono v1
2 |
3 | FxEvents provides a robust and secure framework for event handling in FiveM, equipped with powerful serialization, encryption, and anti-tampering features. With its easy integration and extensive functionality, FxEvents is a valuable tool for developers seeking efficient and secure client-server communication in their FiveM projects. It incorporates encrypted signatures and MsgPack binary serialization to safeguard against malicious client actions. To integrate FxEvents into your project, simply include `FxEvents.Client.dll` or `FxEvents.Server.dll` along with `Newtonsoft.Json.dll` (if you opt for JSON serialization). The MsgPack functionality is inherently supported by FiveM, so no additional libraries are required!
4 |
5 | Starting from Version 3.0.0, the library eliminates the need for manual event initialization, as internal events are now SHA-256 generated based on the resource name combined with a random seed. This update streamlines the process, with initialization primarily used to register events marked with the `[FxEvent]` attribute that would otherwise require a mandatory call from the requesting script.
6 |
7 | For more details and support, [join our Discord server](https://discord.gg/KKN7kRT2vM).
8 |
9 | ---
10 |
11 | **Support**
12 |
13 | If you like my work, please consider supporting me via PayPal. You can [buy me a coffee or donut, some banana, a shirt, BMW i4, Taycan, Tesla, the stars, or whatever you want here](https://ko-fi.com/manups4e).
14 |
15 | ---
16 |
17 | ## Usage Examples:
18 |
19 | ### Initialization
20 |
21 | To initialize the FxEvents library, include the following in your script:
22 |
23 | ```csharp
24 | public class Main : BaseScript
25 | {
26 | public Main()
27 | {
28 | // Initialize the FxEvents library. Call this once at script start to enable EventHub usage anywhere.
29 | EventHub.Initialize();
30 | }
31 | }
32 | ```
33 |
34 | ---
35 |
36 | ### Mounting an Event
37 |
38 | Events can be mounted similarly to standard events. Below is an example of mounting an event in both lambda style and classic method:
39 |
40 | - The `FromSource` attribute enables you to retrieve the source as in standard events, allowing requests for Player, the ISource inheriting class, source as Int32 or as String.
41 |
42 | ```csharp
43 | EventHub.Mount("eventName", Binding.All, new Action([FromSource] source, val1, val2) =>
44 | {
45 | // Code to execute inside the event.
46 | // ISource is an optional class handling clients triggering the event, similar to the "[FromSource] Player player" parameter but customizable.
47 | // On the client-side, the same principle applies without the ClientId parameter.
48 | }));
49 |
50 | EventHub.Mount("eventName", Binding.All, new Action(MyMethod));
51 | private void MyMethod([FromSource] ISource source, type1 val1, type2 val2)
52 | {
53 | // Code to execute inside the event.
54 | // ISource is an optional class handling clients triggering the event, similar to the "[FromSource] Player player" parameter but customizable.
55 | // On the client-side, the same principle applies without the ClientId parameter.
56 | }
57 | ```
58 |
59 | - **Q: Must the returning method mounted with `Func` be `async Task`?**
60 | - **A: Not necessarily. If you don't need it to be a task, return the required type. The `Get` method is awaitable because it waits for a response from the other side.**
61 |
62 | - Events can also be mounted using the `[FxEvent("EventName")]` attribute or by `EventHub.Events["EventName"] += new Action / new Func`.
63 |
64 | ⚠️ Note: Only one method per attribute can be registered for callbacks.
65 |
66 | ---
67 |
68 | ### Event Handling Enhancements
69 |
70 | From version 3.0.0, events are managed similarly to Mono V2 (thanks to @Thorium for ongoing support), allowing for type conversions across sides. Events are no longer bound to specific parameters, enabling casting from one type to another. ⚠️ Generic objects (object or dynamic) are not allowed due to MsgPack restrictions.
71 |
72 | Example:
73 |
74 | ```csharp
75 | [FxEvent("myEvent")]
76 | public static async void GimmeAll(int a, string b)
77 | => Logger.Info($"GimmeAll1 {a} {b}");
78 |
79 | [FxEvent("myEvent")]
80 | public static async void GimmeAll(int a)
81 | => Logger.Info($"GimmeAll1 {a}");
82 |
83 | [FxEvent("myEvent")]
84 | public static async void GimmeAll(string a, int b)
85 | => Logger.Info($"GimmeAll2 {a} {b}");
86 |
87 | [FxEvent("myEvent")]
88 | public static async void GimmeAll(string a, int b, string c = "Hey")
89 | => Logger.Info($"GimmeAll3 {a} {b} {c}");
90 |
91 | [FxEvent("myEvent")]
92 | public static async void GimmeAll(int a, string b, string c = "Oh", int d = 678)
93 | => Logger.Info($"GimmeAll4 {a} {b} {c} {d}");
94 |
95 | [FxEvent("myEvent")]
96 | public static async void GimmeAll(int a, PlayerClient b, string c = "Oh", int d = 678)
97 | => Logger.Info($"GimmeAll5 {a} {b.Player.Name} {c} {d}");
98 |
99 | // Trigger the event
100 | EventHub.Send("myEvent", 1234, 1);
101 | ```
102 |
103 | Outputs:
104 |
105 | 
106 |
107 | ⚠️ Attributed methods MUST be static.
108 |
109 | ### Triggering an Event
110 |
111 | Events can be triggered from both client and server sides:
112 |
113 | ```csharp
114 | // Client-side
115 | EventHub.Send("eventName", params);
116 |
117 | // Server-side
118 | EventHub.Send(Player, "eventName", params);
119 | EventHub.Send(List, "eventName", params);
120 | EventHub.Send(ISource, "eventName", params);
121 | EventHub.Send(List, "eventName", params);
122 | EventHub.Send("eventName", params); // For all connected players
123 | ```
124 |
125 | ### Triggering a Callback
126 |
127 | #### Mounting a Callback
128 |
129 | ```csharp
130 | EventHub.Mount("eventName", Binding.All, new Func>(async ([FromSource] source, val1, val2) =>
131 | {
132 | // Code to execute inside the event.
133 | // ISource is an optional class handling clients triggering the event, similar to the "[FromSource] Player player" parameter but customizable.
134 | // On the client-side, the same principle applies without the ISource parameter.
135 | return val3;
136 | }));
137 | ```
138 |
139 | Callbacks can also be mounted using the `[FxEvent("EventName")]` attribute. ⚠️ Only one per endpoint. Example:
140 |
141 | ```csharp
142 | [FxEvent("myEvent")]
143 | public static string GimmeAll(int a, string b)
144 | => "This is a test";
145 | ```
146 |
147 | #### Calling a Callback
148 |
149 | ```csharp
150 | // Client-side
151 | type param = await EventHub.Get("eventName", params);
152 |
153 | // Server-side
154 | type param = await EventHub.Get(ClientId, "eventName", params);
155 | type param = await EventHub.Get(Player, "eventName", params);
156 | ```
157 |
158 | Callbacks can also be triggered server-side when the server needs information from specific clients.
159 |
160 | Both sides have a new `SendLocal` and `GetLocal` (method requires binding as All or Local) to trigger local events. FxEvents is required in both scripts to work.
161 |
162 | ---
163 |
164 | ### Native ValueTuple Support
165 |
166 | Starting from version 3.0.0, FxEvents offers native ValueTuple support for client-side (server-side using .Net Standard 2.0 already supports it natively). This provides an alternative to non-fixable Tuples that can't be included in collections or as members/fields inside classes and structs. ValueTuple is dynamic, easy to use, supports the latest C# versions, and being natively supported means no need for external NuGet packages or imported libraries to use it with FxEvents and FiveM.
167 |
168 | ---
169 |
170 | ### FiveM Types Support
171 |
172 | FxEvents supports FiveM internal types, allowing you to send Vectors, Quaternions, Matrices, and Entities.
173 |
174 | - **Player support:** You can send a Player object (or an int32/string) and receive a Player object on the other side.
175 | - **Entities support:** You can send a Ped, Vehicle, Prop, or Entity type as long as they're networked. FxEvents handles them using their NetworkID.
176 | - **Vectors and Math structs:** These are handled as float arrays, making them lightweight and fast.
177 |
178 | ---
179 |
180 | ### Event Binding
181 |
182 | Starting from 3.0.0, all events are handled like in Mono V2 update (thanks @thorium) and require a binding to be specified when mounted. There are 4 types of binding:
183 |
184 | - **None:** The event will be ignored and never triggered.
185 | - **Local:** The event is triggered ONLY when called using `SendLocal` / `GetLocal`.
186 | - **Remote:** The event will trigger only for Client/Server events (classic FxEvents way).
187 | - **All:** The event will trigger both with same-side or client/server calls.
188 |
189 | This allows better handling of communications between sides. Legacy EventDispatcher automatically mounts as Remote and `[FxEvent]` attribute binds automatically to All if no binding is specified.
190 |
191 | ---
192 |
193 | ### Anti-Tamper
194 |
195 | In version 3.0.0, a basic Anti-Tamper system is introduced, which uses a server event (`EventHandlers[fxevents:tamperingprotection] += anyMethodYouWant`) with parameters [source, endpoint, TamperType]:
196 |
197 | - **source:** The player handle that triggered the Anti-Tamper.
198 | - **endpoint:** The event that has been flagged as tampered.
199 | - **TamperType:** The type of tamper applied. It can be:
200 | - **TamperType:** The type of tamper applied. It can be:
201 | - **REPEATED_MESSAGE_ID:** Indicates someone tried to send the same event or a new event with a used ID.
202 | - **EDITED_ENCRYPTED_DATA:** Indicates someone altered the encrypted data trying to change values, making it impossible to decrypt.
203 | - **REQUESTED_NEW_PUBLIC_KEY:** Indicates a client tried to request a new ID to change the encryption, potentially attempting to edit the encryption.
204 |
205 | ---
206 |
207 | ### Additional Features for Customization and Debugging
208 |
209 | FxEvents includes several additional features for customization and debugging, especially regarding serialization:
210 |
211 | #### ToJson()
212 |
213 | Requires Newtonsoft.Json:
214 | ```csharp
215 | string text = param.ToJson();
216 | ```
217 |
218 | #### FromJson()
219 |
220 | Requires Newtonsoft.Json:
221 | ```csharp
222 | type value = jsonText.FromJson();
223 | ```
224 |
225 | #### ToBytes()
226 |
227 | ```csharp
228 | byte[] bytes = param.ToBytes();
229 | ```
230 |
231 | #### FromBytes()
232 |
233 | ```csharp
234 | type value = bytes.FromBytes();
235 | ```
236 |
237 | #### EncryptObject(string passkey)
238 |
239 | Binary serialization is performed internally. ⚠️ Same key for encryption and decryption:
240 | ```csharp
241 | byte[] bytes = param.EncryptObject("passkey");
242 | ```
243 |
244 | #### DecryptObject(string passkey)
245 |
246 | Binary deserialization is performed internally. ⚠️ Same key for encryption and decryption:
247 | ```csharp
248 | T object = bytes.DecryptObject("passkey");
249 | ```
250 |
251 | #### GenerateHash(string input)
252 |
253 | Generates the SHA-256 hash of the given input string:
254 | ```csharp
255 | byte[] hash = Encryption.GenerateHash(string input)
256 | ```
257 |
258 | #### BytesToString
259 |
260 | Converts a byte array to a string:
261 | ```csharp
262 | byte[] bytes = param.ToBytes();
263 | string txt = bytes.BytesToString();
264 | ```
265 |
266 | #### StringToBytes
267 |
268 | Converts a string back to a byte array:
269 | ```csharp
270 | byte[] bytes = txt.StringToBytes();
271 | type value = bytes.FromBytes();
272 | ```
273 |
--------------------------------------------------------------------------------
/build/Version.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2.8.2
4 |
5 |
--------------------------------------------------------------------------------
/dependencies/RedM/CitizenFX.Core.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manups4e/FxEvents/27c5d2647b769a16c4a9c38391bae150e79bc757/dependencies/RedM/CitizenFX.Core.dll
--------------------------------------------------------------------------------
/scripts/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manups4e/FxEvents/27c5d2647b769a16c4a9c38391bae150e79bc757/scripts/.gitkeep
--------------------------------------------------------------------------------
/src/FxEvents.Client/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | [*.{cs,vb}]
3 | #### Naming styles ####
4 |
5 | # Naming rules
6 |
7 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
8 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
9 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
10 |
11 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
12 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
13 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
14 |
15 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
16 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
17 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
18 |
19 | # Symbol specifications
20 |
21 | dotnet_naming_symbols.interface.applicable_kinds = interface
22 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
23 | dotnet_naming_symbols.interface.required_modifiers =
24 |
25 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
26 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
27 | dotnet_naming_symbols.types.required_modifiers =
28 |
29 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
30 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
31 | dotnet_naming_symbols.non_field_members.required_modifiers =
32 |
33 | # Naming styles
34 |
35 | dotnet_naming_style.begins_with_i.required_prefix = I
36 | dotnet_naming_style.begins_with_i.required_suffix =
37 | dotnet_naming_style.begins_with_i.word_separator =
38 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
39 |
40 | dotnet_naming_style.pascal_case.required_prefix =
41 | dotnet_naming_style.pascal_case.required_suffix =
42 | dotnet_naming_style.pascal_case.word_separator =
43 | dotnet_naming_style.pascal_case.capitalization = pascal_case
44 |
45 | dotnet_naming_style.pascal_case.required_prefix =
46 | dotnet_naming_style.pascal_case.required_suffix =
47 | dotnet_naming_style.pascal_case.word_separator =
48 | dotnet_naming_style.pascal_case.capitalization = pascal_case
49 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
50 | tab_width = 4
51 | indent_size = 4
52 | end_of_line = crlf
53 | dotnet_style_coalesce_expression = true:suggestion
54 | dotnet_style_null_propagation = true:suggestion
55 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
56 | dotnet_style_prefer_auto_properties = true:silent
57 | dotnet_style_object_initializer = true:suggestion
58 | dotnet_style_collection_initializer = true:suggestion
59 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
60 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
61 | dotnet_style_prefer_conditional_expression_over_return = true:silent
62 | dotnet_style_explicit_tuple_names = true:suggestion
63 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
64 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
65 | dotnet_style_prefer_compound_assignment = true:suggestion
66 | dotnet_style_prefer_simplified_interpolation = true:suggestion
67 | dotnet_style_namespace_match_folder = true:suggestion
68 |
69 | [*.cs]
70 | csharp_indent_labels = one_less_than_current
71 | csharp_using_directive_placement = outside_namespace:silent
72 | csharp_prefer_simple_using_statement = true:suggestion
73 | csharp_prefer_braces = true:silent
74 | csharp_style_namespace_declarations = block_scoped:silent
75 | csharp_style_prefer_method_group_conversion = true:silent
76 | csharp_style_prefer_top_level_statements = true:silent
77 | csharp_style_expression_bodied_methods = false:silent
78 | csharp_style_expression_bodied_constructors = false:silent
79 | csharp_style_expression_bodied_operators = false:silent
80 | csharp_style_expression_bodied_properties = true:silent
81 | csharp_style_expression_bodied_indexers = true:silent
82 | csharp_style_expression_bodied_accessors = true:silent
83 | csharp_style_expression_bodied_lambdas = true:silent
84 | csharp_style_expression_bodied_local_functions = false:silent
85 | csharp_style_throw_expression = true:suggestion
86 | csharp_style_prefer_null_check_over_type_check = true:suggestion
87 | csharp_prefer_simple_default_expression = true:suggestion
88 | csharp_style_prefer_local_over_anonymous_function = true:suggestion
89 | csharp_style_prefer_index_operator = true:suggestion
90 | csharp_style_prefer_range_operator = true:suggestion
91 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
92 | csharp_style_prefer_tuple_swap = true:suggestion
93 | csharp_style_prefer_utf8_string_literals = true:suggestion
94 | csharp_style_inlined_variable_declaration = true:suggestion
95 | csharp_style_deconstructed_variable_declaration = true:suggestion
96 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion
--------------------------------------------------------------------------------
/src/FxEvents.Client/EventDispatcher.cs:
--------------------------------------------------------------------------------
1 | global using CitizenFX.Core;
2 | global using CitizenFX.Core.Native;
3 | using FxEvents.EventSystem;
4 | using FxEvents.Shared;
5 | using FxEvents.Shared.EventSubsystem;
6 |
7 | using Logger;
8 | using System;
9 | using System.Collections.Generic;
10 | using System.Linq;
11 | using System.Linq.Expressions;
12 | using System.Reflection;
13 | using System.Threading.Tasks;
14 |
15 | namespace FxEvents
16 | {
17 | [Obsolete("Use EventHub instead, this class will be removed soon")]
18 | public class EventDispatcher
19 | {
20 | internal static Log Logger;
21 | internal PlayerList GetPlayers => EventHub.Instance.GetPlayers;
22 | internal static ClientGateway Gateway => EventHub.Gateway;
23 | internal static bool Debug => EventHub.Debug;
24 | internal static bool Initialized => EventHub.Initialized;
25 | public static EventsDictionary Events => Gateway._handlers;
26 |
27 | public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent)
28 | {
29 | EventHub.Initialize();
30 | }
31 |
32 | public static void Send(string endpoint, params object[] args)
33 | {
34 | EventHub.Send(endpoint, args);
35 | }
36 |
37 | public static void SendLatent(string endpoint, int bytesPerSeconds, params object[] args)
38 | {
39 | EventHub.SendLatent(endpoint, bytesPerSeconds, args);
40 | }
41 |
42 | public static async Task Get(string endpoint, params object[] args)
43 | {
44 | return await EventHub.Get(endpoint, args);
45 | }
46 | public static void Mount(string endpoint, Delegate @delegate)
47 | {
48 | EventHub.Mount(endpoint, Binding.Remote, @delegate);
49 | }
50 | public static void Unmount(string endpoint)
51 | {
52 | EventHub.Unmount(endpoint);
53 | }
54 |
55 | }
56 | }
--------------------------------------------------------------------------------
/src/FxEvents.Client/EventHub.cs:
--------------------------------------------------------------------------------
1 | global using CitizenFX.Core;
2 | global using CitizenFX.Core.Native;
3 | using FxEvents.EventSystem;
4 | using FxEvents.Shared;
5 | using FxEvents.Shared.Encryption;
6 | using FxEvents.Shared.EventSubsystem;
7 |
8 | using Logger;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Linq.Expressions;
13 | using System.Reflection;
14 | using System.Threading.Tasks;
15 |
16 | namespace FxEvents
17 | {
18 | public class EventHub : ClientScript
19 | {
20 | internal static Log Logger;
21 | internal PlayerList GetPlayers => Players;
22 | internal static ClientGateway Gateway;
23 | internal static bool Debug { get; set; }
24 | internal static bool Initialized = false;
25 | internal static EventHub Instance;
26 | public static EventsDictionary Events => Gateway._handlers;
27 |
28 | public EventHub()
29 | {
30 | Logger = new Log();
31 | Instance = this;
32 | var resName = API.GetCurrentResourceName();
33 | string debugMode = API.GetResourceMetadata(resName, "fxevents_debug_mode", 0);
34 | Debug = debugMode == "yes" || debugMode == "true" || int.TryParse(debugMode, out int num) && num > 0;
35 |
36 | byte[] inbound = Encryption.GenerateHash(resName + "_inbound");
37 | byte[] outbound = Encryption.GenerateHash(resName + "_outbound");
38 | byte[] signature = Encryption.GenerateHash(resName + "_signature");
39 | Gateway = new ClientGateway();
40 | Gateway.SignaturePipeline = signature.BytesToString();
41 | Gateway.InboundPipeline = inbound.BytesToString();
42 | Gateway.OutboundPipeline = outbound.BytesToString();
43 | }
44 |
45 | public static void Initialize()
46 | {
47 | Initialized = true;
48 | Gateway.AddEvents();
49 |
50 | var assembly = Assembly.GetCallingAssembly();
51 | // we keep it outside because multiple classes with same event callback? no sir no.
52 | List withReturnType = new List();
53 |
54 | foreach (var type in assembly.GetTypes())
55 | {
56 | var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
57 | .Where(m => m.GetCustomAttributes(typeof(FxEventAttribute), false).Length > 0);
58 |
59 | foreach (var method in methods)
60 | {
61 | var parameters = method.GetParameters().Select(p => p.ParameterType).ToArray();
62 | var actionType = Expression.GetDelegateType(parameters.Concat(new[] { method.ReturnType }).ToArray());
63 | var attribute = method.GetCustomAttribute();
64 |
65 | if (method.ReturnType != typeof(void))
66 | {
67 | if (withReturnType.Contains(attribute.Name))
68 | {
69 | // throw error and break execution for the script sake.
70 | throw new Exception($"FxEvents - Failed registering [{attribute.Name}] delegates. Cannot register more than 1 delegate for [{attribute.Name}] with a return type!");
71 | }
72 | else
73 | {
74 | withReturnType.Add(attribute.Name);
75 | }
76 | }
77 |
78 | if (method.IsStatic)
79 | Mount(attribute.Name, attribute.Binding, Delegate.CreateDelegate(actionType, method));
80 | else
81 | Logger.Error($"Error registering method {method.Name} - FxEvents supports only Static methods for its [FxEvent] attribute!");
82 | }
83 | }
84 | }
85 |
86 | ///
87 | /// registra un evento client (TriggerEvent)
88 | ///
89 | /// Nome evento
90 | /// Azione legata all'evento
91 | internal async void AddEventHandler(string eventName, Delegate action)
92 | {
93 | while (!Initialized) await BaseScript.Delay(0);
94 | EventHandlers[eventName] += action;
95 | }
96 |
97 | public static void Send(string endpoint, params object[] args)
98 | {
99 | if (!Initialized)
100 | {
101 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
102 | return;
103 | }
104 | Gateway.Send(endpoint, Binding.Remote, args);
105 | }
106 |
107 | public static void SendLocal(string endpoint, params object[] args)
108 | {
109 | if (!Initialized)
110 | {
111 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
112 | return;
113 | }
114 | Gateway.Send(endpoint, Binding.Local, args);
115 | }
116 |
117 | public static void SendLatent(string endpoint, int bytesPerSeconds, params object[] args)
118 | {
119 | if (!Initialized)
120 | {
121 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
122 | return;
123 | }
124 | Gateway.SendLatent(endpoint, bytesPerSeconds, args);
125 | }
126 |
127 | public static async Task Get(string endpoint, params object[] args)
128 | {
129 | if (!Initialized)
130 | {
131 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
132 | return default;
133 | }
134 | return await Gateway.Get(endpoint, args);
135 | }
136 | public static void Mount(string endpoint, Binding binding, Delegate @delegate)
137 | {
138 | if (!Initialized)
139 | {
140 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
141 | return;
142 | }
143 | Gateway.Mount(endpoint, binding, @delegate);
144 | }
145 | public static void Unmount(string endpoint)
146 | {
147 | if (!Initialized)
148 | {
149 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
150 | return;
151 | }
152 | Gateway.Unmount(endpoint);
153 | }
154 |
155 | }
156 | }
--------------------------------------------------------------------------------
/src/FxEvents.Client/EventSystem/ClientGateway.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared;
2 | using FxEvents.Shared.Diagnostics;
3 | using FxEvents.Shared.Encryption;
4 | using FxEvents.Shared.EventSubsystem;
5 | using FxEvents.Shared.Message;
6 | using FxEvents.Shared.Serialization;
7 | using FxEvents.Shared.Serialization.Implementations;
8 | using FxEvents.Shared.Snowflakes;
9 | using System;
10 | using System.Net.Sockets;
11 | using System.Threading.Tasks;
12 |
13 | namespace FxEvents.EventSystem
14 | {
15 | internal class ClientGateway : BaseGateway
16 | {
17 | protected override ISerialization Serialization { get; }
18 |
19 | private EventHub _hub => EventHub.Instance;
20 | private Curve25519 _curve25519;
21 | private byte[] _secret = [];
22 |
23 |
24 | public ClientGateway()
25 | {
26 | SnowflakeGenerator.Create((short)new Random().Next(1, 199));
27 | _curve25519 = Curve25519.Create();
28 | Serialization = new MsgPackSerialization();
29 | DelayDelegate = async delay => await BaseScript.Delay(delay);
30 | PrepareDelegate = PrepareAsync;
31 | PushDelegate = Push;
32 | PushDelegateLatent = PushLatent;
33 | }
34 |
35 | internal void AddEvents()
36 | {
37 | _hub.AddEventHandler(InboundPipeline, new Action(async (endpoint, binding, encrypted) =>
38 | {
39 | try
40 | {
41 | await ProcessInboundAsync(new ServerId().Handle, endpoint, binding, encrypted);
42 | }
43 | catch (Exception ex)
44 | {
45 | EventMessage message = encrypted.DecryptObject();
46 | Logger.Error($"InboundPipeline [{message.Endpoint}]:" + ex.ToString());
47 | }
48 | }));
49 |
50 | _hub.AddEventHandler(OutboundPipeline, new Action((endpoint, binding, serialized) =>
51 | {
52 | try
53 | {
54 | ProcessReply(serialized);
55 | }
56 | catch (Exception ex)
57 | {
58 | Logger.Error("OutboundPipeline:" + ex.ToString());
59 | }
60 | }));
61 |
62 | _hub.AddEventHandler(SignaturePipeline, new Action(signature => _secret = _curve25519.GetSharedSecret(signature)));
63 | BaseScript.TriggerServerEvent(SignaturePipeline, _curve25519.GetPublicKey());
64 | }
65 |
66 | internal async Task PrepareAsync(string pipeline, int source, IMessage message)
67 | {
68 | if (_secret.Length == 0)
69 | {
70 | StopwatchUtil stopwatch = StopwatchUtil.StartNew();
71 | while (_secret.Length == 0) await BaseScript.Delay(0);
72 | if (EventHub.Debug)
73 | {
74 | Logger.Debug($"[{message}] Halted {stopwatch.Elapsed.TotalMilliseconds}ms due to signature retrieval.");
75 | }
76 | }
77 | }
78 |
79 | internal void Push(string pipeline, int source, string endpoint, Binding binding, byte[] buffer)
80 | {
81 | if(binding == Binding.All || binding == Binding.Remote)
82 | {
83 | if(binding != Binding.Remote)
84 | if (source != -1) throw new Exception($"The client can only target server events. (arg {nameof(source)} is not matching -1)");
85 | BaseScript.TriggerServerEvent(pipeline, endpoint, binding, buffer);
86 | }
87 | else if (binding == Binding.All || binding == Binding.Local)
88 | {
89 | BaseScript.TriggerEvent(pipeline, endpoint, binding, buffer);
90 | }
91 | }
92 |
93 | internal void PushLatent(string pipeline, int source, int bytePerSecond, string endpoint, byte[] buffer)
94 | {
95 | if (source != -1) throw new Exception($"The client can only target server events. (arg {nameof(source)} is not matching -1)");
96 | BaseScript.TriggerLatentServerEvent(pipeline, bytePerSecond, endpoint, Binding.Remote, buffer);
97 | }
98 |
99 | public async void Send(string endpoint, Binding binding, params object[] args)
100 | {
101 | await CreateAndSendAsync(EventFlowType.Straight, new ServerId().Handle, endpoint, binding, args);
102 | }
103 |
104 | public async void SendLatent(string endpoint, int bytePerSecond, params object[] args)
105 | {
106 | await CreateAndSendLatentAsync(EventFlowType.Straight, new ServerId().Handle, endpoint, bytePerSecond, args);
107 | }
108 |
109 | public async Task Get(string endpoint, params object[] args)
110 | {
111 | return await GetInternal(new ServerId().Handle, endpoint, Binding.Remote, args);
112 | }
113 |
114 | public async Task GetLocal(string endpoint, params object[] args)
115 | {
116 | return await GetInternal(new ServerId().Handle, endpoint, Binding.Local, args);
117 | }
118 |
119 | internal byte[] GetSecret(int _)
120 | {
121 | if (_secret == null)
122 | throw new Exception("Shared Encryption Secret has not been generated yet");
123 | return _secret;
124 | }
125 | }
126 | }
--------------------------------------------------------------------------------
/src/FxEvents.Client/FxEvents.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Release RedM;Release FiveM;Debug RedM;Debug FiveM
4 | FxEvents FiveM Client
5 | FxEvents RedM Client
6 | FxEvents an advanced event subsystem for FiveM C# Resources.
7 | FxEvents an advanced event subsystem for RedM C# Resources.
8 | FiveM FxEvents
9 | RedM FxEvents
10 | FxEvents.FiveM.Client
11 | FxEvents.RedM.Client
12 | Debug FiveM
13 | AnyCPU
14 | {4F57E7A2-97B7-499B-8F9A-57F0AF74C9AC}
15 | Library
16 | Properties
17 | FxEvents.Client
18 | FxEvents.Client
19 | v4.5.2
20 | 512
21 | True
22 | net452
23 | latest
24 | ..\..\CompiledLibs\Client
25 | FxEvents.Client
26 | https://github.com/manups4e/FxEvents
27 | Copyright Leonardo Emanuele
28 | README.md
29 | embedded
30 | FxEvents.Client
31 | $(WarningsNotAsError);8632;8618;8600;8602;8625
32 | annotations
33 | False
34 | False
35 |
36 |
37 | true
38 | embedded
39 | false
40 | CLIENT
41 | prompt
42 | 4
43 |
44 |
45 | embedded
46 | true
47 | CLIENT
48 | prompt
49 | 4
50 |
51 |
52 |
53 | ..\..\FiveMMsgPack\MsgPack.dll
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | $(PkgNewtonsoft_Json)\lib\portable-net40+sl5+win8+wp8+wpa81\Newtonsoft.Json.dll
64 | False
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | True
74 | \
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/src/FxEvents.Server/.editorconfig:
--------------------------------------------------------------------------------
1 |
2 | [*.cs]
3 | #### Stili di denominazione ####
4 |
5 | # Regole di denominazione
6 |
7 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
8 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
9 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
10 |
11 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
12 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
13 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
14 |
15 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
16 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
17 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
18 |
19 | # Specifiche dei simboli
20 |
21 | dotnet_naming_symbols.interface.applicable_kinds = interface
22 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
23 | dotnet_naming_symbols.interface.required_modifiers =
24 |
25 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
26 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
27 | dotnet_naming_symbols.types.required_modifiers =
28 |
29 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
30 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
31 | dotnet_naming_symbols.non_field_members.required_modifiers =
32 |
33 | # Stili di denominazione
34 |
35 | dotnet_naming_style.begins_with_i.required_prefix = I
36 | dotnet_naming_style.begins_with_i.required_suffix =
37 | dotnet_naming_style.begins_with_i.word_separator =
38 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
39 |
40 | dotnet_naming_style.pascal_case.required_prefix =
41 | dotnet_naming_style.pascal_case.required_suffix =
42 | dotnet_naming_style.pascal_case.word_separator =
43 | dotnet_naming_style.pascal_case.capitalization = pascal_case
44 |
45 | dotnet_naming_style.pascal_case.required_prefix =
46 | dotnet_naming_style.pascal_case.required_suffix =
47 | dotnet_naming_style.pascal_case.word_separator =
48 | dotnet_naming_style.pascal_case.capitalization = pascal_case
49 | csharp_indent_labels = one_less_than_current
50 | csharp_style_expression_bodied_methods = false:silent
51 | csharp_style_expression_bodied_constructors = false:silent
52 | csharp_style_expression_bodied_operators = false:silent
53 | csharp_style_expression_bodied_properties = true:silent
54 | csharp_style_expression_bodied_indexers = true:silent
55 | csharp_style_expression_bodied_accessors = true:silent
56 | csharp_style_expression_bodied_lambdas = true:silent
57 | csharp_style_expression_bodied_local_functions = false:silent
58 | csharp_style_conditional_delegate_call = true:suggestion
59 | csharp_space_around_binary_operators = before_and_after
60 |
61 | [*.vb]
62 | #### Stili di denominazione ####
63 |
64 | # Regole di denominazione
65 |
66 | dotnet_naming_rule.interface_should_be_inizia_con_i.severity = suggestion
67 | dotnet_naming_rule.interface_should_be_inizia_con_i.symbols = interface
68 | dotnet_naming_rule.interface_should_be_inizia_con_i.style = inizia_con_i
69 |
70 | dotnet_naming_rule.tipi_should_be_notazione_pascal.severity = suggestion
71 | dotnet_naming_rule.tipi_should_be_notazione_pascal.symbols = tipi
72 | dotnet_naming_rule.tipi_should_be_notazione_pascal.style = notazione_pascal
73 |
74 | dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.severity = suggestion
75 | dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.symbols = membri_non_di_campo
76 | dotnet_naming_rule.membri_non_di_campo_should_be_notazione_pascal.style = notazione_pascal
77 |
78 | # Specifiche dei simboli
79 |
80 | dotnet_naming_symbols.interface.applicable_kinds = interface
81 | dotnet_naming_symbols.interface.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
82 | dotnet_naming_symbols.interface.required_modifiers =
83 |
84 | dotnet_naming_symbols.tipi.applicable_kinds = class, struct, interface, enum
85 | dotnet_naming_symbols.tipi.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
86 | dotnet_naming_symbols.tipi.required_modifiers =
87 |
88 | dotnet_naming_symbols.membri_non_di_campo.applicable_kinds = property, event, method
89 | dotnet_naming_symbols.membri_non_di_campo.applicable_accessibilities = public, friend, private, protected, protected_friend, private_protected
90 | dotnet_naming_symbols.membri_non_di_campo.required_modifiers =
91 |
92 | # Stili di denominazione
93 |
94 | dotnet_naming_style.inizia_con_i.required_prefix = I
95 | dotnet_naming_style.inizia_con_i.required_suffix =
96 | dotnet_naming_style.inizia_con_i.word_separator =
97 | dotnet_naming_style.inizia_con_i.capitalization = pascal_case
98 |
99 | dotnet_naming_style.notazione_pascal.required_prefix =
100 | dotnet_naming_style.notazione_pascal.required_suffix =
101 | dotnet_naming_style.notazione_pascal.word_separator =
102 | dotnet_naming_style.notazione_pascal.capitalization = pascal_case
103 |
104 | dotnet_naming_style.notazione_pascal.required_prefix =
105 | dotnet_naming_style.notazione_pascal.required_suffix =
106 | dotnet_naming_style.notazione_pascal.word_separator =
107 | dotnet_naming_style.notazione_pascal.capitalization = pascal_case
108 |
109 | [*.{cs,vb}]
110 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
111 | end_of_line = crlf
112 | dotnet_style_coalesce_expression = true:suggestion
113 | dotnet_style_null_propagation = true:suggestion
114 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
115 | dotnet_style_prefer_auto_properties = true:silent
116 | dotnet_style_object_initializer = true:suggestion
117 | dotnet_style_collection_initializer = true:suggestion
118 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
119 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
120 | dotnet_style_prefer_conditional_expression_over_return = true:silent
121 | dotnet_style_explicit_tuple_names = true:suggestion
122 | dotnet_style_prefer_inferred_tuple_names = true:suggestion
123 | dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
124 | tab_width = 4
125 | indent_size = 4
--------------------------------------------------------------------------------
/src/FxEvents.Server/EventDispatcher.cs:
--------------------------------------------------------------------------------
1 | global using CitizenFX.Core;
2 | global using CitizenFX.Core.Native;
3 | using FxEvents.EventSystem;
4 | using FxEvents.Shared;
5 | using FxEvents.Shared.Encryption;
6 | using FxEvents.Shared.EventSubsystem;
7 |
8 | using Logger;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Linq.Expressions;
13 | using System.Reflection;
14 | using System.Threading.Tasks;
15 |
16 | namespace FxEvents
17 | {
18 | [Obsolete("Use EventHub instead, this class will be removed soon")]
19 | public class EventDispatcher
20 | {
21 | internal static Log Logger { get; set; }
22 | internal ExportDictionary GetExports => EventHub.Instance.GetExports;
23 | internal PlayerList GetPlayers => EventHub.Instance.GetPlayers;
24 | internal static ServerGateway Gateway => EventHub.Gateway;
25 | internal static bool Debug => EventHub.Debug;
26 | internal static bool Initialized = EventHub.Initialized;
27 |
28 | public static EventsDictionary Events => EventHub.Gateway._handlers;
29 |
30 | public static void Initalize(string inboundEvent, string outboundEvent, string signatureEvent)
31 | {
32 | EventHub.Initialize();
33 | }
34 |
35 | internal async void RegisterEvent(string eventName, Delegate action)
36 | {
37 | EventHub.Instance.RegisterEvent(eventName, action);
38 | }
39 |
40 | public static void Send(Player player, string endpoint, params object[] args)
41 | {
42 | EventHub.Send(player, endpoint, args);
43 | }
44 | public static void Send(ISource client, string endpoint, params object[] args)
45 | {
46 | EventHub.Send(client, endpoint, args);
47 | }
48 | public static void Send(IEnumerable players, string endpoint, params object[] args)
49 | {
50 | EventHub.Send(players, endpoint, args);
51 | }
52 |
53 | public static void Send(string endpoint, params object[] args)
54 | {
55 | EventHub.Send(endpoint, args);
56 | }
57 |
58 | public static void Send(IEnumerable clients, string endpoint, params object[] args)
59 | {
60 | EventHub.Send(clients, endpoint, args);
61 | }
62 |
63 | public static void SendLatent(Player player, string endpoint, int bytesPerSeconds, params object[] args)
64 | {
65 | EventHub.SendLatent(player, endpoint, bytesPerSeconds, args);
66 | }
67 |
68 | public static void SendLatent(ISource client, string endpoint, int bytesPerSeconds, params object[] args)
69 | {
70 | EventHub.SendLatent(client, endpoint, bytesPerSeconds, args);
71 | }
72 |
73 | public static void SendLatent(IEnumerable players, string endpoint, int bytesPerSeconds, params object[] args)
74 | {
75 | EventHub.SendLatent(players, endpoint, bytesPerSeconds, args);
76 | }
77 |
78 | public static void SendLatent(string endpoint, int bytesPerSeconds, params object[] args)
79 | {
80 | EventHub.SendLatent(endpoint, bytesPerSeconds, args);
81 | }
82 |
83 | public static void SendLatent(IEnumerable clients, string endpoint, int bytesPerSeconds, params object[] args)
84 | {
85 | EventHub.SendLatent(clients, endpoint, bytesPerSeconds, args);
86 | }
87 |
88 | public static async Task Get(Player player, string endpoint, params object[] args)
89 | {
90 | return await EventHub.Get(player, endpoint, args);
91 | }
92 |
93 | public static async Task Get(ISource client, string endpoint, params object[] args)
94 | {
95 | return await EventHub.Get(client, endpoint, args);
96 | }
97 |
98 | public static void Mount(string endpoint, Delegate @delegate)
99 | {
100 | EventHub.Mount(endpoint, Binding.Remote, @delegate);
101 | }
102 | public static void Unmount(string endpoint)
103 | {
104 | EventHub.Unmount(endpoint);
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/src/FxEvents.Server/EventHub.cs:
--------------------------------------------------------------------------------
1 | global using CitizenFX.Core;
2 | global using CitizenFX.Core.Native;
3 | using FxEvents.EventSystem;
4 | using FxEvents.Shared;
5 | using FxEvents.Shared.Encryption;
6 | using FxEvents.Shared.EventSubsystem;
7 |
8 | using Logger;
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Linq;
12 | using System.Linq.Expressions;
13 | using System.Reflection;
14 | using System.Threading.Tasks;
15 |
16 | namespace FxEvents
17 | {
18 | public class EventHub : ServerScript
19 | {
20 | internal static Log Logger { get; set; }
21 | internal ExportDictionary GetExports => Exports;
22 | internal PlayerList GetPlayers => Players;
23 | internal static ServerGateway Gateway { get; set; }
24 | internal static bool Debug { get; set; }
25 | internal static bool Initialized = false;
26 | internal static EventHub Instance;
27 |
28 | public static EventsDictionary Events => Gateway._handlers;
29 |
30 | public EventHub()
31 | {
32 | Logger = new Log();
33 | Instance = this;
34 | var resName = API.GetCurrentResourceName();
35 | string debugMode = API.GetResourceMetadata(resName, "fxevents_debug_mode", 0);
36 | Debug = debugMode == "yes" || debugMode == "true" || int.TryParse(debugMode, out int num) && num > 0;
37 | API.RegisterCommand("generatekey", new Action, string>(async (a, b, c) =>
38 | {
39 | if (a != 0) return;
40 | Logger.Info("Generating random passfrase with a 50 words dictionary...");
41 | Tuple ret = await Encryption.GenerateKey();
42 | string print = $"Here is your generated encryption key, save it in a safe place.\nThis key is not saved by FXEvents anywhere, so please store it somewhere safe, if you save encrypted data and loose this key, your data will be lost.\n" +
43 | $"You can always generate new keys by using \"generatekey\" command.\n" +
44 | $"Passfrase: {ret.Item1}\nEncrypted Passfrase: {ret.Item2}";
45 | Logger.Info(print);
46 | }), false);
47 | byte[] inbound = Encryption.GenerateHash(resName + "_inbound");
48 | byte[] outbound = Encryption.GenerateHash(resName + "_outbound");
49 | byte[] signature = Encryption.GenerateHash(resName + "_signature");
50 | Gateway = new ServerGateway();
51 | Gateway.SignaturePipeline = signature.BytesToString();
52 | Gateway.InboundPipeline = inbound.BytesToString();
53 | Gateway.OutboundPipeline = outbound.BytesToString();
54 | EventHandlers.Add("playerJoining", new Action(OnPlayerDropped));
55 | EventHandlers.Add("playerDropped", new Action(OnPlayerDropped));
56 | }
57 |
58 | public static void Initialize()
59 | {
60 | Initialized = true;
61 | Gateway.AddEvents();
62 |
63 | var assembly = Assembly.GetCallingAssembly();
64 | List withReturnType = new List();
65 | foreach (var type in assembly.GetTypes())
66 | {
67 | var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).Where(m => m.GetCustomAttributes(typeof(FxEventAttribute), false).Length > 0);
68 |
69 | foreach (var method in methods)
70 | {
71 | var parameters = method.GetParameters().Select(p => p.ParameterType).ToArray();
72 | var actionType = Expression.GetDelegateType(parameters.Concat(new[] { method.ReturnType }).ToArray());
73 | var attribute = method.GetCustomAttribute();
74 |
75 | if (method.ReturnType != typeof(void))
76 | {
77 | if (withReturnType.Contains(attribute.Name))
78 | {
79 | // throw error and break execution for the script sake.
80 | throw new Exception($"FxEvents - Failed registering [{attribute.Name}] delegates. Cannot register more than 1 delegate for [{attribute.Name}] with a return type!");
81 | }
82 | else
83 | {
84 | withReturnType.Add(attribute.Name);
85 | }
86 | }
87 |
88 | if (method.IsStatic)
89 | {
90 | Mount(attribute.Name, attribute.Binding, Delegate.CreateDelegate(actionType, method));
91 | }
92 | else
93 | Logger.Error($"Error registering method {method.Name} - FxEvents supports only Static methods for its [FxEvent] attribute!");
94 | }
95 | }
96 | }
97 |
98 | ///
99 | /// Register an event (TriggerEvent)
100 | ///
101 | /// Event name
102 | /// Action bound to the event
103 | internal async void RegisterEvent(string eventName, Delegate action)
104 | {
105 | while (!Initialized) await BaseScript.Delay(0);
106 | EventHandlers[eventName] += action;
107 | }
108 |
109 | public static void Send(Player player, string endpoint, params object[] args)
110 | {
111 | if (!Initialized)
112 | {
113 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
114 | return;
115 | }
116 | Gateway.Send(player, endpoint, args);
117 | }
118 | public static void Send(ISource client, string endpoint, params object[] args)
119 | {
120 | if (!Initialized)
121 | {
122 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
123 | return;
124 | }
125 | Gateway.Send(client, endpoint, args);
126 | }
127 | public static void Send(IEnumerable players, string endpoint, params object[] args)
128 | {
129 | if (!Initialized)
130 | {
131 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
132 | return;
133 | }
134 | Gateway.Send(players.ToList(), endpoint, args);
135 | }
136 |
137 | public static void Send(string endpoint, params object[] args)
138 | {
139 | if (!Initialized)
140 | {
141 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
142 | return;
143 | }
144 |
145 | PlayerList playerList = Instance.GetPlayers;
146 | Gateway.Send(playerList.ToList(), endpoint, args);
147 | }
148 |
149 | public static void Send(IEnumerable clients, string endpoint, params object[] args)
150 | {
151 | if (!Initialized)
152 | {
153 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
154 | return;
155 | }
156 | Gateway.Send(clients.ToList(), endpoint, args);
157 | }
158 |
159 | public static void SendLocal(string endpoint, params object[] args)
160 | {
161 | if (!Initialized)
162 | {
163 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
164 | return;
165 | }
166 | Gateway.Send(endpoint, args);
167 | }
168 |
169 | public static void SendLatent(Player player, string endpoint, int bytesPerSeconds, params object[] args)
170 | {
171 | if (!Initialized)
172 | {
173 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
174 | return;
175 | }
176 | Gateway.SendLatent(Convert.ToInt32(player.Handle), endpoint, bytesPerSeconds, args);
177 | }
178 |
179 | public static void SendLatent(ISource client, string endpoint, int bytesPerSeconds, params object[] args)
180 | {
181 | if (!Initialized)
182 | {
183 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
184 | return;
185 | }
186 | Gateway.SendLatent(client.Handle, endpoint, bytesPerSeconds, args);
187 | }
188 |
189 | public static void SendLatent(IEnumerable players, string endpoint, int bytesPerSeconds, params object[] args)
190 | {
191 | if (!Initialized)
192 | {
193 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
194 | return;
195 | }
196 | Gateway.SendLatent(players.Select(x => Convert.ToInt32(x.Handle)).ToList(), endpoint, bytesPerSeconds, args);
197 | }
198 |
199 | public static void SendLatent(string endpoint, int bytesPerSeconds, params object[] args)
200 | {
201 | if (!Initialized)
202 | {
203 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
204 | return;
205 | }
206 | PlayerList playerList = Instance.GetPlayers;
207 | Gateway.SendLatent(playerList.Select(x => Convert.ToInt32(x.Handle)).ToList(), endpoint, bytesPerSeconds, args);
208 | }
209 |
210 | public static void SendLatent(IEnumerable clients, string endpoint, int bytesPerSeconds, params object[] args)
211 | {
212 | if (!Initialized)
213 | {
214 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
215 | return;
216 | }
217 | Gateway.SendLatent(clients.Select(x => x.Handle).ToList(), endpoint, bytesPerSeconds, args);
218 | }
219 |
220 | public static async Task Get(Player player, string endpoint, params object[] args)
221 | {
222 | if (!Initialized)
223 | {
224 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
225 | return default;
226 | }
227 | return await Gateway.Get(Convert.ToInt32(player.Handle), endpoint, args);
228 | }
229 |
230 | public static async Task Get(ISource client, string endpoint, params object[] args)
231 | {
232 | if (!Initialized)
233 | {
234 | Logger.Error("Dispatcher not initialized, please initialize it and add the events strings");
235 | return default;
236 | }
237 | return await Gateway.Get(client.Handle, endpoint, args);
238 | }
239 |
240 | public static void Mount(string endpoint, Binding binding, Delegate @delegate)
241 | {
242 | Gateway.Mount(endpoint, binding, @delegate);
243 | }
244 | public static void Unmount(string endpoint)
245 | {
246 | Gateway.Unmount(endpoint);
247 | }
248 |
249 | private void OnPlayerDropped([FromSource] Player player)
250 | {
251 | if (Gateway._signatures.ContainsKey(int.Parse(player.Handle)))
252 | Gateway._signatures.Remove(int.Parse(player.Handle));
253 | }
254 | }
255 | }
--------------------------------------------------------------------------------
/src/FxEvents.Server/EventSystem/ServerGateway.cs:
--------------------------------------------------------------------------------
1 | using CitizenFX.Core;
2 | using FxEvents.Shared;
3 | using FxEvents.Shared.Diagnostics;
4 | using FxEvents.Shared.Encryption;
5 | using FxEvents.Shared.EventSubsystem;
6 | using FxEvents.Shared.Message;
7 | using FxEvents.Shared.Serialization;
8 | using FxEvents.Shared.Serialization.Implementations;
9 | using FxEvents.Shared.Snowflakes;
10 | using FxEvents.Shared.TypeExtensions;
11 | using System;
12 | using System.Collections.Generic;
13 | using System.Linq;
14 | using System.Net;
15 | using System.Security.Cryptography;
16 | using System.Threading.Tasks;
17 |
18 | namespace FxEvents.EventSystem
19 | {
20 | public class ServerGateway : BaseGateway
21 | {
22 | protected override ISerialization Serialization { get; }
23 | internal Dictionary _signatures;
24 |
25 | private EventHub _hub => EventHub.Instance;
26 |
27 | public ServerGateway()
28 | {
29 | SnowflakeGenerator.Create((short)new Random().Next(200, 399));
30 | Serialization = new MsgPackSerialization();
31 | DelayDelegate = async delay => await BaseScript.Delay(delay);
32 | PrepareDelegate = PrepareAsync;
33 | PushDelegate = Push;
34 | PushDelegateLatent = PushLatent;
35 | _signatures = new();
36 | }
37 |
38 | internal void AddEvents()
39 | {
40 | _hub.RegisterEvent(SignaturePipeline, new Action(GetSignature));
41 | _hub.RegisterEvent(InboundPipeline, new Action(Inbound));
42 | _hub.RegisterEvent(OutboundPipeline, new Action(Outbound));
43 | }
44 |
45 | internal void Push(string pipeline, int source, string endpoint, Binding binding, byte[] buffer)
46 | {
47 | if (binding == Binding.All || binding == Binding.Remote)
48 | {
49 | if (source != new ServerId().Handle)
50 | BaseScript.TriggerClientEvent(_hub.GetPlayers[source], pipeline, endpoint, binding, buffer);
51 | else
52 | BaseScript.TriggerClientEvent(pipeline, endpoint, binding, buffer);
53 | }
54 | else if (binding == Binding.All || binding == Binding.Local)
55 | {
56 | BaseScript.TriggerEvent(pipeline, endpoint, binding, buffer);
57 | }
58 | }
59 |
60 | internal void PushLatent(string pipeline, int source, int bytePerSecond, string endpoint, byte[] buffer)
61 | {
62 | if (source != new ServerId().Handle)
63 | BaseScript.TriggerLatentClientEvent(_hub.GetPlayers[source], pipeline, bytePerSecond, endpoint, Binding.Remote, buffer);
64 | else
65 | BaseScript.TriggerLatentClientEvent(pipeline, bytePerSecond, endpoint, Binding.Remote, buffer);
66 | }
67 |
68 | private void GetSignature([FromSource] string source, byte[] clientPubKey)
69 | {
70 | try
71 | {
72 | int client = int.Parse(source.Replace("net:", string.Empty));
73 | if (_signatures.ContainsKey(client))
74 | {
75 | Logger.Warning($"Client {API.GetPlayerName("" + client)}[{client}] tried acquiring event signature more than once.");
76 | BaseScript.TriggerEvent("fxevents:tamperingprotection", source, "signature retrival", TamperType.REQUESTED_NEW_PUBLIC_KEY);
77 | return;
78 | }
79 |
80 | Curve25519 curve25519 = Curve25519.Create();
81 | byte[] secret = curve25519.GetSharedSecret(clientPubKey);
82 |
83 | _signatures.Add(client, secret);
84 |
85 | BaseScript.TriggerClientEvent(_hub.GetPlayers[client], SignaturePipeline, curve25519.GetPublicKey());
86 | }
87 | catch (Exception ex)
88 | {
89 | Logger.Error(ex.ToString());
90 | }
91 | }
92 |
93 | private async void Inbound([FromSource] string source, string endpoint, Binding binding, byte[] encrypted)
94 | {
95 | try
96 | {
97 | int client = -1;
98 | if(source != null)
99 | {
100 | client = int.Parse(source.Replace("net:", string.Empty));
101 |
102 | if (!_signatures.TryGetValue(client, out byte[] signature))
103 | return;
104 | }
105 |
106 | try
107 | {
108 | await ProcessInboundAsync(client, endpoint, binding, encrypted);
109 | }
110 | catch (TimeoutException)
111 | {
112 | API.DropPlayer(client.ToString(), $"Operation timed out: {endpoint.ToBase64()}");
113 | }
114 | }
115 | catch (Exception ex)
116 | {
117 | Logger.Error(ex.ToString());
118 | }
119 | }
120 |
121 | private void Outbound([FromSource] string source, string endpoint, Binding binding, byte[] encrypted)
122 | {
123 | try
124 | {
125 | int client = int.Parse(source.Replace("net:", string.Empty));
126 |
127 | if (!_signatures.TryGetValue(client, out byte[] signature)) return;
128 |
129 | EventResponseMessage response = encrypted.DecryptObject(client);
130 |
131 | ProcessReply(response);
132 | }
133 | catch (Exception ex)
134 | {
135 | Logger.Error(ex.ToString());
136 | }
137 | }
138 |
139 | public void Send(Player player, string endpoint, params object[] args) => Send(Convert.ToInt32(player.Handle), endpoint, Binding.Remote, args);
140 | public void Send(ISource client, string endpoint, params object[] args) => Send(client.Handle, endpoint, Binding.Remote, args);
141 | public void Send(List players, string endpoint, params object[] args) => Send(players.Select(x => int.Parse(x.Handle)).ToList(), endpoint, Binding.Remote, args);
142 | public void Send(List clients, string endpoint, params object[] args) => Send(clients.Select(x => x.Handle).ToList(), endpoint, Binding.Remote, args);
143 | public void Send(string endpoint, params object[] args) => Send([], endpoint, Binding.Local, args);
144 |
145 | public async void Send(List targets, string endpoint, Binding binding, params object[] args)
146 | {
147 | if (binding == Binding.Remote)
148 | {
149 | int i = 0;
150 | while (i < targets.Count)
151 | {
152 | await BaseScript.Delay(0);
153 | Send(targets[i], endpoint, binding, args);
154 | i++;
155 | }
156 | }
157 | else if (binding == Binding.Local)
158 | {
159 | Send(-1, endpoint, binding, args);
160 | }
161 | }
162 |
163 | public async void Send(int target, string endpoint, Binding binding, params object[] args)
164 | {
165 | if (!string.IsNullOrWhiteSpace(EventHub.Instance.GetPlayers[target].Name) || (binding == Binding.Local))
166 | await CreateAndSendAsync(EventFlowType.Straight, target, endpoint, binding, args);
167 | }
168 |
169 | public void SendLatent(Player player, string endpoint, int bytesxSecond, params object[] args) => SendLatent(Convert.ToInt32(player.Handle), endpoint, bytesxSecond, args);
170 | public void SendLatent(ISource client, string endpoint, int bytesxSecond, params object[] args) => SendLatent(client.Handle, endpoint, bytesxSecond, args);
171 | public void SendLatent(List players, string endpoint, int bytesxSecond, params object[] args) => SendLatent(players.Select(x => Convert.ToInt32(x.Handle)).ToList(), endpoint, bytesxSecond, args);
172 | public void SendLatent(List clients, string endpoint, int bytesxSecond, params object[] args) => SendLatent(clients.Select(x => x.Handle).ToList(), endpoint, bytesxSecond, args);
173 |
174 | public async void SendLatent(List targets, string endpoint, int bytesxSecond, params object[] args)
175 | {
176 | int i = 0;
177 | while (i < targets.Count)
178 | {
179 | await BaseScript.Delay(0);
180 | SendLatent(targets[i], endpoint, bytesxSecond, args);
181 | i++;
182 | }
183 | }
184 |
185 | public async void SendLatent(int target, string endpoint, int bytesxSecond, params object[] args)
186 | {
187 | if (!string.IsNullOrWhiteSpace(EventHub.Instance.GetPlayers[target].Name))
188 | await CreateAndSendLatentAsync(EventFlowType.Straight, target, endpoint, bytesxSecond, args);
189 | }
190 |
191 | public Task Get(Player player, string endpoint, params object[] args) =>
192 | Get(Convert.ToInt32(player.Handle), endpoint, args);
193 |
194 | public Task Get(ISource client, string endpoint, params object[] args) =>
195 | Get(client.Handle, endpoint, args);
196 |
197 | public async Task Get(int target, string endpoint, params object[] args)
198 | {
199 | return await GetInternal(target, endpoint, Binding.Remote, args);
200 | }
201 |
202 | public async Task GetLocal(string endpoint, params object[] args)
203 | {
204 | return await GetInternal(-1, endpoint, Binding.Local, args);
205 | }
206 |
207 | internal async Task PrepareAsync(string pipeline, int source, IMessage message)
208 | {
209 | if (GetSecret(source).Length == 0)
210 | {
211 | StopwatchUtil stopwatch = StopwatchUtil.StartNew();
212 | long time = API.GetGameTimer();
213 | while (GetSecret(source).Length == 0)
214 | {
215 | if (API.GetGameTimer() - time > 1000)
216 | {
217 | if (EventHub.Debug)
218 | {
219 | Logger.Debug($"[{message}] Took to much time: {stopwatch.Elapsed.TotalMilliseconds}ms due to signature retrieval, client not found, still connecting or disconnected.");
220 | }
221 | return;
222 | }
223 | await BaseScript.Delay(0);
224 | }
225 | if (EventHub.Debug)
226 | {
227 | Logger.Debug($"[{message}] Halted {stopwatch.Elapsed.TotalMilliseconds}ms due to signature retrieval.");
228 | }
229 | }
230 | }
231 |
232 |
233 | internal byte[] GetSecret(int source)
234 | {
235 | if (!_signatures.ContainsKey(source)) {
236 | Curve25519 curve25519 = Curve25519.Create();
237 | byte[] secret = curve25519.GetSharedSecret(curve25519.GetPublicKey());
238 | return secret;
239 | }
240 | return _signatures[source];
241 | }
242 | }
243 | }
--------------------------------------------------------------------------------
/src/FxEvents.Server/FxEvents.Server.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | FxEvents Server
5 | FxEvents an advanced event subsystem for FiveM C# Resources.
6 | README.md
7 | FxEvents.Server
8 | FxEvents.Server
9 | https://github.com/manups4e/FxEvents
10 | https://github.com/manups4e/FxEvents
11 | Copyright Leonardo Emanuele
12 | netstandard2.0
13 | latest
14 | FxEvents.Server
15 | FxEvents.Server
16 | ..\..\CompiledLibs\Server
17 | annotations
18 | Debug;Release
19 | False
20 | False
21 |
22 |
23 |
24 | x64
25 | SERVER
26 | portable
27 |
28 |
29 |
30 | SERVER
31 | portable
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ..\..\FiveMMsgPack\MsgPack.dll
40 |
41 |
42 | $(PkgNewtonsoft_Json)\lib\portable-net40+sl5+win8+wp8+wpa81\Newtonsoft.Json.dll
43 | False
44 |
45 |
46 |
47 |
48 | True
49 | \
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/BinaryHelper.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.Serialization;
2 | using FxEvents.Shared.Serialization.Implementations;
3 | using System;
4 | using System.Globalization;
5 | using System.Linq;
6 |
7 | namespace FxEvents.Shared
8 | {
9 | public static class BinaryHelper
10 | {
11 | private static MsgPackSerialization msgpackSerialization = new();
12 |
13 | public static byte[] ToBytes(this T obj)
14 | {
15 | using SerializationContext context = new("BinaryHelper", "ToBytes", msgpackSerialization);
16 | context.Serialize(typeof(T), obj);
17 | return context.GetData();
18 | }
19 | public static byte[] StringToBytes(this string str)
20 | {
21 | char[] arr = str.ToCharArray();
22 | if (arr[2] != '-' && arr[5] != '-')
23 | return Enumerable.Range(0, str.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(str.Substring(x, 2), 16)).ToArray();
24 | return str.Split('-').Select(x => byte.Parse(x, NumberStyles.HexNumber)).ToArray();
25 | }
26 |
27 | public static string BytesToString(this byte[] ba, bool separator = false, bool toLower = true)
28 | {
29 | string bytes;
30 | if (separator)
31 | bytes = BitConverter.ToString(ba);
32 | else
33 | bytes = BitConverter.ToString(ba).Replace("-", "");
34 |
35 | if (toLower)
36 | bytes = bytes.ToLower();
37 | return bytes;
38 | }
39 |
40 | public static T FromBytes(this byte[] data)
41 | {
42 | using SerializationContext context = new(data.ToString(), "FromBytes", msgpackSerialization, data);
43 | return context.Deserialize();
44 | }
45 |
46 | public static ulong ToUInt64(this byte[] bytes)
47 | {
48 | if (bytes == null)
49 | throw new ArgumentNullException(nameof(bytes));
50 | if (bytes.Length > sizeof(ulong))
51 | throw new ArgumentException("Must be 8 elements or fewer", nameof(bytes));
52 |
53 | ulong result = 0;
54 | for (int i = 0; i < bytes.Length; i++)
55 | {
56 | result |= (ulong)bytes[i] << (i * 8);
57 | }
58 | return result;
59 | }
60 |
61 | public static byte[] FromUInt64(this ulong num)
62 | {
63 | byte[] buffer = new byte[sizeof(ulong)];
64 | for (int i = 0; i < sizeof(ulong); i++)
65 | {
66 | buffer[i] = (byte)(num >> (i * 8));
67 | }
68 | return buffer;
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/ContractResolvers.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json.Serialization;
2 | using System;
3 |
4 | namespace FxEvents.Shared
5 | {
6 | public class ContractResolver : DefaultContractResolver
7 | {
8 | protected override JsonContract CreateContract(Type objectType)
9 | {
10 | JsonContract contract = base.CreateContract(objectType);
11 |
12 | if (objectType.IsAbstract || objectType.IsInterface)
13 | {
14 | Type substitute = JsonHelper.Substitutes.TryGetValue(objectType, out Type result) ? result : null;
15 |
16 | if (substitute != null)
17 | {
18 | contract.Converter = new TypeConverter(substitute);
19 | }
20 | }
21 |
22 | return contract;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/Encryption/Curve25519.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Encryption
4 | {
5 | ///
6 | /// Elliptic curve Curve25519 with Diffie Hellman key exchange scheme.
7 | ///
8 | public class Curve25519
9 | {
10 | ///
11 | /// Elliptic curve Curve25519 with Diffie Hellman key exchange scheme.
12 | ///
13 | public const string Curve25519Sha256 = "curve25519-sha256";
14 |
15 | ///
16 | /// Creates a new instance of class.
17 | ///
18 | /// Algorithm name. Only is supported.
19 | public static Curve25519 Create(string algorithmName = Curve25519Sha256)
20 | {
21 | if (Curve25519Sha256 == algorithmName)
22 | return new Curve25519();
23 |
24 | return null;
25 | }
26 |
27 | private byte[] _privateKey;
28 |
29 | ///
30 | /// Gets algorithm name.
31 | ///
32 | public string Name
33 | {
34 | get { return Curve25519Sha256; }
35 | }
36 |
37 | private void EnsurePrivateKey()
38 | {
39 | if (_privateKey == null)
40 | _privateKey = Curve25519Inner.CreateRandomPrivateKey();
41 | }
42 |
43 | ///
44 | /// Returns public key.
45 | ///
46 | public byte[] GetPublicKey()
47 | {
48 | EnsurePrivateKey();
49 | return Curve25519Inner.GetPublicKey(_privateKey);
50 | }
51 |
52 | ///
53 | /// Returns private key.
54 | ///
55 | public byte[] GetPrivateKey()
56 | {
57 | EnsurePrivateKey();
58 | return (byte[])_privateKey.Clone();
59 | }
60 |
61 | ///
62 | /// Initializes the algorithm from public key.
63 | ///
64 | public void FromPublicKey(byte[] publicKey)
65 | {
66 | throw new NotSupportedException();
67 | }
68 |
69 | ///
70 | /// Initializes the algorithm from private key.
71 | ///
72 | public void FromPrivateKey(byte[] privateKey)
73 | {
74 | if (privateKey == null)
75 | throw new ArgumentNullException("privateKey");
76 |
77 | _privateKey = (byte[])privateKey.Clone();
78 | }
79 |
80 | ///
81 | /// Returns shared secret for other party's public key and own private key.
82 | ///
83 | public byte[] GetSharedSecret(byte[] otherPublicKey)
84 | {
85 | if (otherPublicKey == null)
86 | throw new ArgumentNullException("otherPublicKey");
87 |
88 | EnsurePrivateKey();
89 | return Curve25519Inner.GetSharedSecret(_privateKey, otherPublicKey);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/Encryption/Encryption.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace FxEvents.Shared.Encryption
9 | {
10 | public static class Encryption
11 | {
12 | static readonly Random random = new Random(DateTime.Now.Millisecond);
13 | #region Byte encryption
14 | private static byte[] GenerateIV()
15 | {
16 | byte[] rgbIV = new byte[16];
17 | using (RNGCryptoServiceProvider rng = new())
18 | rng.GetBytes(rgbIV);
19 | return rgbIV;
20 | }
21 |
22 | private static byte[] EncryptBytes(byte[] data, object input)
23 | {
24 | byte[] rgbIV = GenerateIV();
25 | byte[] keyBytes = input switch
26 | {
27 | int sourceId => EventHub.Gateway.GetSecret(sourceId),
28 | string strKey => GenerateHash(strKey),
29 | _ => throw new ArgumentException("Input must be an int or a string.", nameof(input)),
30 | };
31 | using AesManaged aesAlg = new AesManaged
32 | {
33 | Key = keyBytes,
34 | IV = rgbIV
35 | };
36 |
37 | ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
38 |
39 | using MemoryStream msEncrypt = new MemoryStream();
40 | msEncrypt.Write(rgbIV, 0, rgbIV.Length);
41 | using CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
42 | csEncrypt.Write(data, 0, data.Length);
43 | csEncrypt.FlushFinalBlock();
44 |
45 | return msEncrypt.ToArray();
46 | }
47 |
48 | private static byte[] DecryptBytes(byte[] data, object input)
49 | {
50 | byte[] rgbIV = data.Take(16).ToArray(); // Extract the IV from the beginning of the data
51 | byte[] keyBytes = input switch
52 | {
53 | int sourceId => EventHub.Gateway.GetSecret(sourceId),
54 | string strKey => GenerateHash(strKey),
55 | _ => throw new ArgumentException("Input must be an int or a string.", nameof(input)),
56 | };
57 | using AesManaged aesAlg = new AesManaged
58 | {
59 | Key = keyBytes,
60 | IV = rgbIV
61 | };
62 |
63 | ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
64 |
65 | using MemoryStream msDecrypt = new MemoryStream(data.Skip(16).ToArray()); // Skip the IV part
66 | using CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);
67 | using MemoryStream msDecrypted = new MemoryStream();
68 | csDecrypt.CopyTo(msDecrypted);
69 |
70 | return msDecrypted.ToArray();
71 | }
72 | #endregion
73 |
74 |
75 | internal static byte[] EncryptObject(this T obj, int plySource = -1)
76 | {
77 | return EncryptBytes(obj.ToBytes(), plySource);
78 | }
79 |
80 | internal static T DecryptObject(this byte[] data, int plySource = -1)
81 | {
82 | return DecryptBytes(data, plySource).FromBytes();
83 | }
84 |
85 |
86 | ///
87 | /// Encrypt the object.
88 | ///
89 | ///
90 | /// The object to encrypt.
91 | /// The string key to encrypt it.
92 | ///
93 | /// An encrypted array of byte
94 | public static byte[] EncryptObject(this T obj, string key)
95 | {
96 | if (string.IsNullOrWhiteSpace(key))
97 | throw new Exception("FXEvents: Encryption key cannot be empty!");
98 | return EncryptBytes(obj.ToBytes(), key);
99 | }
100 |
101 | ///
102 | /// Decrypt the object.
103 | ///
104 | ///
105 | /// The data to decrypt.
106 | /// The key to decrypt it (MUST BE THE SAME AS THE ENCRYPTION KEY).
107 | ///
108 | /// A
109 | public static T DecryptObject(this byte[] data, string key)
110 | {
111 | if (string.IsNullOrWhiteSpace(key))
112 | throw new Exception("FXEvents: Encryption key cannot be empty!");
113 | return EncryptBytes(data, key).FromBytes();
114 | }
115 |
116 | ///
117 | /// Generate the Sha-256 hash of the given input string.
118 | ///
119 | /// The input string.
120 | /// The generated hash in byte[]
121 | public static byte[] GenerateHash(string input)
122 | {
123 | using SHA256Managed sha256 = new SHA256Managed();
124 | return sha256.ComputeHash(Encoding.UTF8.GetBytes(input));
125 | }
126 |
127 | internal static async Task> GenerateKey()
128 | {
129 | string[] words = ["Scalder", "Suscipient", "Sodalite", "Maharanis", "Mussier", "Abouts", "Geologized", "Antivenins", "Volcanized", "Heliskier", "Bedclothes", "Streamier", "Postulant", "Grizzle", "Folkies", "Poplars", "Stalls", "Chiefess", "Trip", "Untarred", "Cadillacs", "Fixings", "Overage", "Upbraider", "Phocas", "Galton", "Pests", "Saxifraga", "Erodes", "Bracketing", "Rugs", "Deprecate", "Monomials", "Subtracts", "Kettledrum", "Cometic", "Wrvs", "Phalangids", "Vareuse", "Pinchbecks", "Moony", "Scissoring", "Sarks", "Victresses", "Thorned", "Bowled", "Bakeries", "Printable", "Beethoven", "Sacher"];
130 | int i = 0;
131 | int length = random.Next(5, 10);
132 | string passfrase = "";
133 | while (i <= length)
134 | {
135 | await BaseScript.Delay(5);
136 | string symbol = "";
137 | if (i > 0)
138 | symbol = "-";
139 | passfrase += symbol + words[random.Next(words.Length - 1)];
140 | i++;
141 | }
142 | return new(passfrase, passfrase.EncryptObject(GetRandomString(random.Next(30, 50))).BytesToString());
143 | }
144 |
145 | private static string GetRandomString(int size, bool lowerCase = false)
146 | {
147 | StringBuilder builder = new StringBuilder(size);
148 | // Unicode/ASCII Letters are divided into two blocks
149 | // (Letters 65�90 / 97�122):
150 | // The first group containing the uppercase letters and
151 | // the second group containing the lowercase.
152 |
153 | // char is a single Unicode character
154 | char offset = lowerCase ? 'a' : 'A';
155 | const int lettersOffset = 26; // A...Z or a..z: length=26
156 |
157 | for (int i = 0; i < size; i++)
158 | {
159 | char @char = (char)random.Next(offset, offset + lettersOffset);
160 | builder.Append(@char);
161 | }
162 |
163 | return lowerCase ? builder.ToString().ToLower() : builder.ToString();
164 | }
165 | }
166 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Attributes/ForceAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Attributes
4 | {
5 | ///
6 | /// Indicates that this property should be forcefully added to serialization.
7 | ///
8 | [Obsolete("the current MsgPack version does not consent to serialize non public members/fields")]
9 | public class ForceAttribute : Attribute
10 | {
11 | }
12 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Attributes/FxEventAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace FxEvents.Shared
6 | {
7 | [Flags]
8 | public enum Binding
9 | {
10 | ///
11 | /// No one can call this
12 | ///
13 | None = 0x0,
14 |
15 | ///
16 | /// Server only accepts server calls, client only client calls
17 | ///
18 | Local = 0x1,
19 |
20 | ///
21 | /// Server only accepts client calls, client only server calls
22 | ///
23 | Remote = 0x2,
24 |
25 | ///
26 | /// Accept all incoming calls
27 | ///
28 | All = Local | Remote
29 | }
30 |
31 | ///
32 | /// The fxevent attribute.
33 | ///
34 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
35 | public class FxEventAttribute : Attribute
36 | {
37 | public string Name { get; }
38 | public Binding Binding { get; }
39 | public FxEventAttribute(string name, Binding binding = Binding.All)
40 | {
41 | Name = name;
42 | Binding = binding;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Attributes/IgnoreAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Attributes
4 | {
5 | ///
6 | /// Indicates that this property should be disregarded from serialization.
7 | ///
8 | [Obsolete("the current MsgPack version does not consent to ignore non public members/fields")]
9 | public class IgnoreAttribute : Attribute
10 | {
11 | }
12 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/BaseGatewayHelpers.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using System;
4 | using System.Globalization;
5 |
6 | internal static class BaseGatewayHelpers
7 | {
8 |
9 | internal static object GetHolder(MessagePackObject msgpkObj, Type type)
10 | {
11 | object obj = msgpkObj.ToObject();
12 | TypeCode typeCode = Type.GetTypeCode(type);
13 | switch (typeCode)
14 | {
15 | case TypeCode.String:
16 | if (msgpkObj.IsNil)
17 | return string.Empty;
18 | return obj as string ?? (type.IsSimpleType() ? obj.ToString() : string.Empty);
19 | case TypeCode.Byte:
20 | case TypeCode.SByte:
21 | case TypeCode.Int16:
22 | case TypeCode.Int32:
23 | case TypeCode.Int64:
24 | case TypeCode.UInt16:
25 | case TypeCode.UInt32:
26 | case TypeCode.UInt64:
27 | if (obj is IConvertible convertible)
28 | {
29 | try
30 | {
31 | return Convert.ChangeType(convertible, type);
32 | }
33 | catch (InvalidCastException)
34 | {
35 | return GetDefaultForType(type);
36 | }
37 | }
38 | else
39 | return GetDefaultForType(type);
40 | case TypeCode.Boolean:
41 | bool booleanValue;
42 | if (bool.TryParse(obj.ToString(), out booleanValue))
43 | return booleanValue;
44 | else
45 | return false;
46 | case TypeCode.Char:
47 | char charValue;
48 | if (char.TryParse(obj.ToString(), out charValue))
49 | return charValue;
50 | else
51 | return '\0';
52 | case TypeCode.Decimal:
53 | decimal decimalValue;
54 | if (decimal.TryParse(obj.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out decimalValue))
55 | return decimalValue;
56 | else
57 | return 0M;
58 | case TypeCode.Single:
59 | float floatValue;
60 | if (float.TryParse(obj.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out floatValue))
61 | return floatValue;
62 | else
63 | return 0F;
64 | case TypeCode.Double:
65 | double doubleValue;
66 | if (double.TryParse(obj.ToString(), NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue))
67 | return doubleValue;
68 | else
69 | return 0D;
70 | case TypeCode.DBNull:
71 | case TypeCode.DateTime:
72 | return obj;
73 | default:
74 | return GetDefaultForType(type);
75 | }
76 | }
77 | private static object GetDefaultForType(Type type)
78 | {
79 | // Determine the default value for the given type
80 | if (type == typeof(string)) return string.Empty;
81 | if (type == typeof(byte)) return 0;
82 | if (type == typeof(sbyte)) return 0;
83 | if (type == typeof(bool)) return false;
84 | if (type == typeof(char)) return '\0';
85 | if (type == typeof(DateTime)) return DateTime.MinValue;
86 | if (type == typeof(decimal)) return 0M;
87 | if (type == typeof(float)) return 0F;
88 | if (type == typeof(double)) return 0D;
89 | if (type == typeof(short)) return 0;
90 | if (type == typeof(int)) return 0;
91 | if (type == typeof(long)) return 0L;
92 | if (type == typeof(ushort)) return 0U;
93 | if (type == typeof(uint)) return 0U;
94 | if (type == typeof(ulong)) return 0UL;
95 | return null; // Fallback to null for unsupported types
96 | }
97 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Diagnostics/IEventLogger.cs:
--------------------------------------------------------------------------------
1 | namespace FxEvents.Shared.Diagnostics
2 | {
3 | public interface IEventLogger
4 | {
5 | void Debug(params object[] values);
6 | void Info(params object[] values);
7 | }
8 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Diagnostics/Impl/ClientStopwatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Diagnostics.Impl
4 | {
5 | internal class ClientStopwatch : StopwatchUtil
6 | {
7 | private long _startTicks;
8 | private long _totalPauseTicks;
9 |
10 | public override TimeSpan Elapsed
11 | {
12 | get
13 | {
14 | long currentTicks = GetTimestamp();
15 | long elapsedTicks = currentTicks - _startTicks - _totalPauseTicks;
16 | return TimeSpan.FromTicks(elapsedTicks);
17 | }
18 | }
19 |
20 | public ClientStopwatch()
21 | {
22 | _startTicks = GetTimestamp();
23 | }
24 |
25 | public override void Stop()
26 | {
27 | _totalPauseTicks += GetTimestamp() - _startTicks;
28 | }
29 |
30 | public override void Start()
31 | {
32 | _startTicks = GetTimestamp() - _totalPauseTicks;
33 | }
34 |
35 | internal static long GetTimestamp()
36 | {
37 | return DateTime.UtcNow.Ticks;
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Diagnostics/Impl/ServerStopwatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace FxEvents.Shared.Diagnostics.Impl
5 | {
6 | internal class ServerStopwatch : StopwatchUtil
7 | {
8 | private readonly Stopwatch _stopwatch;
9 | public override TimeSpan Elapsed => _stopwatch.Elapsed;
10 |
11 | public ServerStopwatch()
12 | {
13 | _stopwatch = Stopwatch.StartNew();
14 | }
15 |
16 | public override void Stop()
17 | {
18 | _stopwatch.Stop();
19 | }
20 |
21 | public override void Start()
22 | {
23 | _stopwatch.Start();
24 | }
25 |
26 | internal static long GetTimestamp()
27 | {
28 | return Stopwatch.GetTimestamp() / 10000;
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Diagnostics/StopwatchUtil.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.Diagnostics.Impl;
2 | using System;
3 |
4 | namespace FxEvents.Shared.Diagnostics
5 | {
6 | public abstract class StopwatchUtil
7 | {
8 | private static bool IsServer = API.IsDuplicityVersion();
9 |
10 | public abstract TimeSpan Elapsed { get; }
11 | public abstract void Stop();
12 | public abstract void Start();
13 |
14 |
15 | public static long Timestamp
16 | {
17 | get
18 | {
19 | if (IsServer)
20 | {
21 | return ServerStopwatch.GetTimestamp();
22 | }
23 | else
24 | {
25 | return ClientStopwatch.GetTimestamp();
26 | }
27 | }
28 | }
29 |
30 | public static StopwatchUtil StartNew()
31 | {
32 | if (IsServer)
33 | {
34 | return new ServerStopwatch();
35 | }
36 |
37 | return new ClientStopwatch();
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/EventsDictionary.cs:
--------------------------------------------------------------------------------
1 |
2 | using FxEvents.Shared.Exceptions;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace FxEvents.Shared.EventSubsystem
11 | {
12 | public class EventsDictionary : Dictionary
13 | {
14 | public new EventEntry this[string key]
15 | {
16 | get
17 | {
18 | var lookupKey = key.ToLower();
19 |
20 | if (this.ContainsKey(lookupKey))
21 | {
22 | return base[lookupKey];
23 | }
24 |
25 | var entry = new EventEntry(key);
26 | base.Add(lookupKey, entry);
27 |
28 | return entry;
29 | }
30 | set { }
31 | }
32 |
33 | public void Add(string endpoint, Binding binding, Delegate callback)
34 | {
35 | this[endpoint] += new Tuple(callback,binding);
36 | }
37 | }
38 |
39 | public class EventEntry
40 | {
41 | internal readonly string m_eventName;
42 | internal readonly List> m_callbacks = new();
43 | internal string name => m_eventName;
44 |
45 | public EventEntry(string eventName)
46 | {
47 | m_eventName = eventName;
48 | }
49 |
50 | public static EventEntry operator +(EventEntry entry, Tuple deleg)
51 | {
52 | entry.m_callbacks.Add(deleg);
53 |
54 | return entry;
55 | }
56 |
57 | public static EventEntry operator -(EventEntry entry, Tuple deleg)
58 | {
59 | entry.m_callbacks.Remove(deleg);
60 |
61 | return entry;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Exceptions/EventException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Exceptions
4 | {
5 | public class EventTimeoutException : Exception
6 | {
7 | public EventTimeoutException(string message) : base(message)
8 | {
9 | }
10 |
11 | public EventTimeoutException(string message, Exception innerException) : base(message, innerException)
12 | {
13 | }
14 | }
15 |
16 | public class EventException : Exception
17 | {
18 | public EventException() { }
19 | public EventException(string message) : base(message) { }
20 | public EventException(string message, Exception innerException) : base(message, innerException) { }
21 | }
22 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/ISerializable.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.Snowflakes;
2 |
3 | namespace FxEvents.Shared.EventSubsystem
4 | {
5 |
6 | public interface IMessage
7 | {
8 | Snowflake Id { get; set; }
9 | string Endpoint { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/ISource.cs:
--------------------------------------------------------------------------------
1 | namespace FxEvents.Shared.EventSubsystem
2 | {
3 | public interface ISource
4 | {
5 | int Handle { get; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Message/EventFlowType.cs:
--------------------------------------------------------------------------------
1 | namespace FxEvents.Shared.Message
2 | {
3 | public enum EventFlowType
4 | {
5 | Straight,
6 | Circular
7 | }
8 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Message/EventMessage.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.EventSubsystem;
2 |
3 | using FxEvents.Shared.Payload;
4 | using FxEvents.Shared.Snowflakes;
5 | using System.Collections.Generic;
6 |
7 | namespace FxEvents.Shared.Message
8 | {
9 | internal class EventMessage : IMessage
10 | {
11 | public Snowflake Id { get; set; }
12 | public string? Endpoint { get; set; }
13 | public EventFlowType Flow { get; set; }
14 | public EventRemote Sender { get; set; }
15 |
16 | public IEnumerable Parameters { get; set; }
17 | public EventMessage() { }
18 | public EventMessage(string endpoint, EventFlowType flow, IEnumerable parameters, EventRemote sender)
19 | {
20 | Id = Snowflake.Next();
21 | Endpoint = endpoint;
22 | Flow = flow;
23 | Parameters = parameters;
24 | Sender = sender;
25 | }
26 |
27 | public override string ToString() => Endpoint;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Message/EventParameter.cs:
--------------------------------------------------------------------------------
1 | namespace FxEvents.Shared.Payload
2 | {
3 | public class EventParameter
4 | {
5 | public byte[] Data { get; set; }
6 |
7 | public EventParameter() { }
8 | public EventParameter(byte[] data)
9 | {
10 | Data = data;
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Message/EventRemote.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace FxEvents.Shared.EventSubsystem
6 | {
7 | internal enum EventRemote
8 | {
9 | Client,
10 | Server
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Message/EventResponseMessage.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.EventSubsystem;
2 | using FxEvents.Shared.Snowflakes;
3 | using System;
4 | using System.IO;
5 |
6 | namespace FxEvents.Shared.Message
7 | {
8 | public class EventResponseMessage : IMessage
9 | {
10 | public Snowflake Id { get; set; }
11 | public string Endpoint { get; set; }
12 | public byte[]? Data { get; set; }
13 |
14 | public EventResponseMessage() { }
15 | public EventResponseMessage(Snowflake id, string endpoint, byte[]? data)
16 | {
17 | Id = id;
18 | Endpoint = endpoint;
19 | Data = data;
20 | }
21 |
22 | public override string ToString() => Id.ToString();
23 | }
24 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Models/EventObservable.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.EventSubsystem;
2 | using System;
3 |
4 | namespace FxEvents.Shared.Models
5 | {
6 | public class EventObservable
7 | {
8 | public IMessage Message { get; set; }
9 | public Action Callback { get; set; }
10 |
11 | public EventObservable(IMessage message, Action callback)
12 | {
13 | Message = message;
14 | Callback = callback;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Models/EventValueHolder.cs:
--------------------------------------------------------------------------------
1 | namespace FxEvents.Shared.Models
2 | {
3 | public class EventValueHolder
4 | {
5 | public byte[] Data { get; set; }
6 | public T Value { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/ISerialization.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace FxEvents.Shared.Serialization
4 | {
5 | public interface ISerialization
6 | {
7 | void Serialize(Type type, object value, SerializationContext context);
8 | void Serialize(T value, SerializationContext context);
9 | object Deserialize(Type type, SerializationContext context);
10 | T Deserialize(SerializationContext context);
11 | }
12 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/JsonSerialization.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System;
3 | using System.Text;
4 |
5 | namespace FxEvents.Shared.Serialization.Implementations
6 | {
7 | public class JsonSerialization : ISerialization
8 | {
9 | public JsonSerialization()
10 | {
11 | }
12 |
13 | public void Serialize(Type type, object value, SerializationContext context)
14 | {
15 | context.Writer.Write(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)));
16 | }
17 |
18 | public void Serialize(T value, SerializationContext context)
19 | {
20 | Serialize(typeof(T), value, context);
21 | }
22 |
23 | public object Deserialize(Type type, SerializationContext context)
24 | {
25 | return JsonConvert.DeserializeObject(
26 | Encoding.UTF8.GetString(context.Reader.ReadBytes(context.Original!.Length)), type);
27 | }
28 |
29 | public T Deserialize(SerializationContext context)
30 | {
31 | return (T)Deserialize(typeof(T), context);
32 | }
33 | }
34 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/DoubleFixer.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using Newtonsoft.Json.Linq;
5 | using System;
6 | using System.Globalization;
7 |
8 | namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPack.MsgPackResolvers
9 | {
10 | public class DoubleFixer : MessagePackSerializer
11 | {
12 | public DoubleFixer(SerializationContext ownerContext) : base(ownerContext)
13 | {
14 | }
15 |
16 | protected override void PackToCore(Packer packer, double objectTree)
17 | {
18 | packer.Pack(objectTree.ToString("G17", CultureInfo.InvariantCulture));
19 | }
20 |
21 | protected override double UnpackFromCore(Unpacker unpacker)
22 | {
23 | var data = unpacker.LastReadData;
24 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
25 | throw new Exception($"FxEvents double - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(int).FullName}");
26 | if (unpacker.IsArrayHeader)
27 | throw new Exception($"FxEvents double - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(int).FullName}");
28 |
29 | return double.Parse(data.AsString(), CultureInfo.InvariantCulture);
30 |
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/EntityResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using System;
5 |
6 | namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers
7 | {
8 | ///
9 | /// For this one and all its derivates.. we use NetworkID to keep consistency between each side.
10 | ///
11 | public class EntityResolver : MessagePackSerializer
12 | {
13 | public EntityResolver(SerializationContext ownerContext) : base(ownerContext)
14 | {
15 | }
16 |
17 | protected override void PackToCore(Packer packer, Entity objectTree)
18 | {
19 | packer.Pack(objectTree.NetworkId);
20 | }
21 |
22 | protected override Entity UnpackFromCore(Unpacker unpacker)
23 | {
24 | var data = unpacker.LastReadData;
25 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
26 | throw new Exception($"FxEvents Entity - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(int).FullName}");
27 | if (unpacker.IsArrayHeader)
28 | throw new Exception($"FxEvents Entity - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(int).FullName}");
29 |
30 | int.TryParse(data.ToObject().ToString(), out int item);
31 | return Entity.FromNetworkId(item);
32 | }
33 | }
34 |
35 | public class PedResolver : MessagePackSerializer
36 | {
37 | public PedResolver(SerializationContext ownerContext) : base(ownerContext)
38 | {
39 | }
40 |
41 | protected override void PackToCore(Packer packer, Ped objectTree)
42 | {
43 | packer.Pack(objectTree.NetworkId);
44 | }
45 |
46 | protected override Ped UnpackFromCore(Unpacker unpacker)
47 | {
48 | var data = unpacker.LastReadData;
49 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
50 | throw new Exception($"FxEvents Ped - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(int).FullName}");
51 | if (unpacker.IsArrayHeader)
52 | throw new Exception($"FxEvents Ped - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(int).FullName}");
53 |
54 | int.TryParse(data.ToObject().ToString(), out int item);
55 | return (Ped)Entity.FromNetworkId(item);
56 | }
57 | }
58 |
59 | public class VehicleResolver : MessagePackSerializer
60 | {
61 | public VehicleResolver(SerializationContext ownerContext) : base(ownerContext)
62 | {
63 | }
64 |
65 | protected override void PackToCore(Packer packer, Vehicle objectTree)
66 | {
67 | packer.Pack(objectTree.NetworkId);
68 | }
69 |
70 | protected override Vehicle UnpackFromCore(Unpacker unpacker)
71 | {
72 | var data = unpacker.LastReadData;
73 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
74 | throw new Exception($"FxEvents Vehicle - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(int).FullName}");
75 | if (unpacker.IsArrayHeader)
76 | throw new Exception($"FxEvents Vehicle - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(int).FullName}");
77 |
78 | int.TryParse(data.ToObject().ToString(), out int item);
79 | return (Vehicle)Entity.FromNetworkId(item);
80 | }
81 | }
82 |
83 | public class PropResolver : MessagePackSerializer
84 | {
85 | public PropResolver(SerializationContext ownerContext) : base(ownerContext)
86 | {
87 | }
88 |
89 | protected override void PackToCore(Packer packer, Prop objectTree)
90 | {
91 | packer.Pack(objectTree.NetworkId);
92 | }
93 |
94 | protected override Prop UnpackFromCore(Unpacker unpacker)
95 | {
96 | var data = unpacker.LastReadData;
97 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
98 | throw new Exception($"FxEvents Prop - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(int).FullName}");
99 | if (unpacker.IsArrayHeader)
100 | throw new Exception($"FxEvents Prop - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(int).FullName}");
101 |
102 | int.TryParse(data.ToObject().ToString(), out int item);
103 | return (Prop)Entity.FromNetworkId(item);
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/ISourceResolver.cs:
--------------------------------------------------------------------------------
1 | //using MsgPack;
2 | //using MsgPack.Serialization;
3 | //using System;
4 | //using System.Collections.Generic;
5 | //using System.Text;
6 |
7 | //namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers
8 | //{
9 | // internal class ISourceResolver : MessagePackSerializer
10 | // {
11 | // public ISourceResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
12 | // {
13 | // }
14 |
15 | // protected override void PackToCore(Packer packer, ISource objectTree)
16 | // {
17 | // packer.Pack(objectTree.Handle);
18 | // }
19 |
20 | // protected override ISource UnpackFromCore(Unpacker unpacker)
21 | // {
22 | // return (ISource)Activator.CreateInstance(typeof(ISource), unpacker.LastReadData.AsInt32());
23 | // }
24 | // }
25 | //}
26 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/KeyValuePairResolver.cs:
--------------------------------------------------------------------------------
1 | //using MsgPack;
2 | //using MsgPack.Serialization;
3 | //using System.Collections.Generic;
4 |
5 | //namespace FxEvents.Shared.Serialization.Implementations.MsgPackResolvers
6 | //{
7 | // public class KeyValuePairResolver : MessagePackSerializer>
8 | // {
9 | // private readonly MessagePackSerializer _keySerializer;
10 | // private readonly MessagePackSerializer _valueSerializer;
11 | // public KeyValuePairResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
12 | // {
13 | // }
14 |
15 | // protected override void PackToCore(Packer packer, KeyValuePair objectTree)
16 | // {
17 | // packer.PackArrayHeader(2);
18 | // this._keySerializer.PackTo(packer, objectTree.Key);
19 | // this._valueSerializer.PackTo(packer, objectTree.Value);
20 | // }
21 |
22 | // protected override KeyValuePair UnpackFromCore(Unpacker unpacker)
23 | // {
24 | // if (!unpacker.Read())
25 | // {
26 | // return default;
27 | // }
28 |
29 | // TKey key = unpacker.LastReadData.IsNil ? default(TKey) : this._keySerializer.UnpackFrom(unpacker);
30 |
31 | // if (!unpacker.Read())
32 | // {
33 | // return default;
34 | // }
35 |
36 | // TValue value = unpacker.LastReadData.IsNil ? default(TValue) : this._valueSerializer.UnpackFrom(unpacker);
37 |
38 | // return new KeyValuePair(key, value);
39 | // }
40 |
41 | // }
42 | //}
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/Matrix3x3Resolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 |
8 | namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers
9 | {
10 | internal class Matrix3x3Resolver : MessagePackSerializer
11 | {
12 | public Matrix3x3Resolver(SerializationContext ownerContext) : base(ownerContext)
13 | {
14 | }
15 |
16 | protected override void PackToCore(Packer packer, Matrix3x3 objectTree)
17 | {
18 | packer.PackArray(objectTree.ToArray());
19 | }
20 |
21 | protected override Matrix3x3 UnpackFromCore(Unpacker unpacker)
22 | {
23 | float[] values = new float[9];
24 | for (int i = 0; i < 9; i++)
25 | {
26 | float item;
27 | if (!unpacker.Read())
28 | {
29 | item = 0;
30 | }
31 | else
32 | {
33 | var data = unpacker.LastReadData;
34 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
35 | throw new Exception($"FxEvents Matrix3x3 - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(float).FullName}");
36 | if (unpacker.IsArrayHeader)
37 | throw new Exception($"FxEvents Matrix3x3 - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(float).FullName}");
38 |
39 | float.TryParse(data.ToObject().ToString(), out item);
40 | }
41 | values[i] = item;
42 | }
43 | return new Matrix3x3(values);
44 |
45 | }
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/MatrixResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Text;
7 |
8 | namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers
9 | {
10 | internal class MatrixResolver : MessagePackSerializer
11 | {
12 | public MatrixResolver(SerializationContext ownerContext) : base(ownerContext)
13 | {
14 | }
15 |
16 | protected override void PackToCore(Packer packer, Matrix objectTree)
17 | {
18 | packer.PackArray(objectTree.ToArray());
19 | }
20 |
21 | protected override Matrix UnpackFromCore(Unpacker unpacker)
22 | {
23 | float[] values = new float[16];
24 | for (int i = 0; i < 16; i++)
25 | {
26 | float item;
27 | if (!unpacker.Read())
28 | {
29 | item = 0;
30 | }
31 | else
32 | {
33 | var data = unpacker.LastReadData;
34 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
35 | throw new Exception($"FxEvents Matrix - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(float).FullName}");
36 | if (unpacker.IsArrayHeader)
37 | throw new Exception($"FxEvents Matrix - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(float).FullName}");
38 |
39 | float.TryParse(data.ToObject().ToString(), out item);
40 | }
41 | values[i] = item;
42 | }
43 | return new Matrix(values);
44 |
45 | }
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/PlayerResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using System;
5 |
6 | namespace FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers
7 | {
8 | public class PlayerResolver : MessagePackSerializer
9 | {
10 | public PlayerResolver(SerializationContext ownerContext) : base(ownerContext)
11 | {
12 | }
13 |
14 | protected override void PackToCore(Packer packer, Player objectTree)
15 | {
16 | #if CLIENT
17 | packer.Pack(objectTree.ServerId);
18 | #elif SERVER
19 | packer.Pack(int.Parse(objectTree.Handle));
20 | #endif
21 | }
22 |
23 | protected override Player UnpackFromCore(Unpacker unpacker)
24 | {
25 | var data = unpacker.LastReadData;
26 | if (!TypeCache.IsSimpleType(data.UnderlyingType))
27 | throw new Exception($"Cannot deserialize type {data.UnderlyingType.Name} into Player type");
28 | string last = data.ToObject().ToString();
29 | int.TryParse(last, out int handle);
30 | return EventHub.Instance.GetPlayers[handle];
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/QuaternionResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using MsgPack;
3 | using MsgPack.Serialization;
4 | using System;
5 |
6 | namespace FxEvents.Shared.Serialization.Implementations.MsgPackResolvers
7 | {
8 | public class QuaternionResolver : MessagePackSerializer
9 | {
10 | private readonly MessagePackSerializer _itemSerializer;
11 | public QuaternionResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
12 | {
13 | this._itemSerializer = ownerContext.GetSerializer();
14 | }
15 |
16 | protected override void PackToCore(Packer packer, Quaternion objectTree)
17 | {
18 | packer.PackArray(objectTree.ToArray());
19 | }
20 |
21 | protected override Quaternion UnpackFromCore(Unpacker unpacker)
22 | {
23 | float[] values = new float[4];
24 | for (int i = 0; i < 4; i++)
25 | {
26 | float item;
27 | if (!unpacker.Read())
28 | {
29 | item = 0;
30 | }
31 | else
32 | {
33 | var data = unpacker.LastReadData;
34 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
35 | throw new Exception($"Cannot deserialize type {data.UnderlyingType.Name} into Quaternion float parameter type");
36 | if(unpacker.IsArrayHeader)
37 | throw new Exception($"Cannot deserialize type array {data.UnderlyingType.Name}[] into Quaternion float parameter type");
38 |
39 | float.TryParse(data.ToObject().ToString(), out item);
40 | }
41 | values[i] = item;
42 | }
43 | return new Quaternion(values);
44 | }
45 |
46 | }
47 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/SnowflakeResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.Snowflakes;
2 | using FxEvents.Shared.TypeExtensions;
3 | using MsgPack;
4 | using MsgPack.Serialization;
5 | using System;
6 |
7 | namespace FxEvents.Shared.Serialization.Implementations.MsgPackResolvers
8 | {
9 | public class SnowflakeResolver : MessagePackSerializer
10 | {
11 | public SnowflakeResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
12 | {
13 | }
14 |
15 | protected override void PackToCore(Packer packer, Snowflake objectTree)
16 | {
17 | packer.Pack(objectTree.ToInt64());
18 | }
19 |
20 | protected override Snowflake UnpackFromCore(Unpacker unpacker)
21 | {
22 | var data = unpacker.LastReadData;
23 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
24 | throw new Exception($"FxEvents Snowflake - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(ulong).FullName}");
25 | if (unpacker.IsArrayHeader)
26 | throw new Exception($"FxEvents Snowflake - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(ulong).FullName}");
27 |
28 | ulong.TryParse(data.ToObject().ToString(), out ulong item);
29 |
30 | return new Snowflake(item);
31 | }
32 |
33 | }
34 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/ValueTupleResolver.cs:
--------------------------------------------------------------------------------
1 | //using Logger;
2 | //using MsgPack;
3 | //using MsgPack.Serialization;
4 | //using System;
5 |
6 | //namespace FxEvents.Shared.Serialization.Implementations.MsgPackResolvers
7 | //{
8 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
9 | // public class ValueTupleResolver : MessagePackSerializer>
10 | // {
11 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
12 | // {
13 | // }
14 |
15 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
16 | // {
17 | // packer.Pack(new object[] { objectTree.Item1 });
18 | // }
19 |
20 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
21 | // {
22 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
23 | // return new ValueTuple((A)obj[0]);
24 | // }
25 | // }
26 |
27 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
28 | // public class ValueTupleResolver : MessagePackSerializer>
29 | // {
30 | // Log logger = new Log();
31 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
32 | // {
33 |
34 | // }
35 |
36 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
37 | // {
38 | // logger.Info("sono chiamato");
39 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2 });
40 | // }
41 |
42 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
43 | // {
44 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
45 | // return new ValueTuple((A)obj[0], (B)obj[1]);
46 | // }
47 | // }
48 |
49 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
50 | // public class ValueTupleResolver : MessagePackSerializer>
51 | // {
52 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
53 | // {
54 | // }
55 |
56 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
57 | // {
58 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2, objectTree.Item3 });
59 | // }
60 |
61 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
62 | // {
63 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
64 | // return new ValueTuple((A)obj[0], (B)obj[1], (C)obj[2]);
65 | // }
66 | // }
67 |
68 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
69 | // public class ValueTupleResolver : MessagePackSerializer>
70 | // {
71 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
72 | // {
73 | // }
74 |
75 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
76 | // {
77 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2, objectTree.Item3, objectTree.Item4 });
78 | // }
79 |
80 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
81 | // {
82 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
83 | // return new ValueTuple((A)obj[0], (B)obj[1], (C)obj[2], (D)obj[3]);
84 | // }
85 | // }
86 |
87 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
88 | // public class ValueTupleResolver : MessagePackSerializer>
89 | // {
90 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
91 | // {
92 | // }
93 |
94 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
95 | // {
96 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2, objectTree.Item3, objectTree.Item4, objectTree.Item5 });
97 | // }
98 |
99 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
100 | // {
101 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
102 | // return new ValueTuple((A)obj[0], (B)obj[1], (C)obj[2], (D)obj[3], (E)obj[4]);
103 | // }
104 | // }
105 |
106 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
107 | // public class ValueTupleResolver : MessagePackSerializer>
108 | // {
109 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
110 | // {
111 | // }
112 |
113 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
114 | // {
115 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2, objectTree.Item3, objectTree.Item4, objectTree.Item5, objectTree.Item6 });
116 | // }
117 |
118 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
119 | // {
120 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
121 | // return new ValueTuple((A)obj[0], (B)obj[1], (C)obj[2], (D)obj[3], (E)obj[4], (F)obj[5]);
122 | // }
123 | // }
124 |
125 | // [Obsolete("Ignored by messagepack apparently due to its non generic behaviour, kept for reference and other uses")]
126 | // public class ValueTupleResolver : MessagePackSerializer>
127 | // {
128 | // public ValueTupleResolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
129 | // {
130 | // }
131 |
132 | // protected override void PackToCore(Packer packer, ValueTuple objectTree)
133 | // {
134 | // packer.Pack(new object[] { objectTree.Item1, objectTree.Item2, objectTree.Item3, objectTree.Item4, objectTree.Item5, objectTree.Item6, objectTree.Item7 });
135 | // }
136 |
137 | // protected override ValueTuple UnpackFromCore(Unpacker unpacker)
138 | // {
139 | // object[] obj = (object[])unpacker.LastReadData.ToObject();
140 | // return new ValueTuple((A)obj[0], (B)obj[1], (C)obj[2], (D)obj[3], (E)obj[4], (F)obj[5], (G)obj[6]);
141 | // }
142 | // }
143 | //}
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPack/MsgPackResolvers/VectorResolver.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.TypeExtensions;
2 | using Logger;
3 | using MsgPack;
4 | using MsgPack.Serialization;
5 | using System;
6 | using System.Linq;
7 |
8 | namespace FxEvents.Shared.Serialization.Implementations.MsgPackResolvers
9 | {
10 | public class Vector2Resolver : MessagePackSerializer
11 | {
12 | public Vector2Resolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
13 | {
14 | }
15 |
16 | protected override void PackToCore(Packer packer, Vector2 objectTree)
17 | {
18 | packer.PackArray(objectTree.ToArray());
19 | }
20 |
21 |
22 | protected override Vector2 UnpackFromCore(Unpacker unpacker)
23 | {
24 | float[] values = new float[2];
25 | for (int i = 0; i < 2; i++)
26 | {
27 | float item;
28 | if (!unpacker.Read())
29 | {
30 | item = 0;
31 | }
32 | else
33 | {
34 | var data = unpacker.LastReadData;
35 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
36 | throw new Exception($"FxEvents Vector2 - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(float).FullName}");
37 | if (unpacker.IsArrayHeader)
38 | throw new Exception($"FxEvents Vector2 - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(float).FullName}");
39 |
40 | float.TryParse(data.ToObject().ToString(), out item);
41 | }
42 | values[i] = item;
43 | }
44 | return new Vector2(values);
45 | }
46 |
47 | }
48 |
49 | public class Vector3Resolver : MessagePackSerializer
50 | {
51 | public Vector3Resolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
52 | {
53 | }
54 |
55 | protected override void PackToCore(Packer packer, Vector3 objectTree)
56 | {
57 | packer.PackArray(objectTree.ToArray());
58 | }
59 |
60 |
61 | protected override Vector3 UnpackFromCore(Unpacker unpacker)
62 | {
63 | float[] values = new float[3];
64 | for (int i = 0; i < 3; i++)
65 | {
66 | float item;
67 | if (!unpacker.Read())
68 | {
69 | item = 0;
70 | }
71 | else
72 | {
73 | var data = unpacker.LastReadData;
74 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
75 | throw new Exception($"FxEvents Vector3 - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(float).FullName}");
76 | if (unpacker.IsArrayHeader)
77 | throw new Exception($"FxEvents Vector3 - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(float).FullName}");
78 |
79 | float.TryParse(data.ToObject().ToString(), out item);
80 | }
81 | values[i] = item;
82 | }
83 | return new Vector3(values);
84 | }
85 |
86 | }
87 | public class Vector4Resolver : MessagePackSerializer
88 | {
89 | public Vector4Resolver(MsgPack.Serialization.SerializationContext ownerContext) : base(ownerContext)
90 | {
91 | }
92 |
93 | protected override void PackToCore(Packer packer, Vector4 objectTree)
94 | {
95 | packer.PackArray(objectTree.ToArray());
96 | }
97 |
98 |
99 | protected override Vector4 UnpackFromCore(Unpacker unpacker)
100 | {
101 | float[] values = new float[4];
102 | for (int i = 0; i < 4; i++)
103 | {
104 | float item;
105 | if (!unpacker.Read())
106 | {
107 | item = 0;
108 | }
109 | else
110 | {
111 | var data = unpacker.LastReadData;
112 | if (!TypeCache.IsSimpleType(data.UnderlyingType) || unpacker.IsMapHeader)
113 | throw new Exception($"FxEvents Vector4 - Cannot deserialize {data.UnderlyingType.FullName} into {typeof(float).FullName}");
114 | if (unpacker.IsArrayHeader)
115 | throw new Exception($"FxEvents Vector4 - Cannot deserialize {data.UnderlyingType.FullName}[] array into {typeof(float).FullName}");
116 |
117 | float.TryParse(data.ToObject().ToString(), out item);
118 | }
119 | values[i] = item;
120 | }
121 | return new Vector4(values);
122 | }
123 |
124 | }
125 | }
--------------------------------------------------------------------------------
/src/FxEvents.Shared/EventSubsystem/Serialization/Implementations/MsgPackSerialization.cs:
--------------------------------------------------------------------------------
1 | using FxEvents.Shared.EventSubsystem.Serialization;
2 | using FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPack.MsgPackResolvers;
3 | using FxEvents.Shared.EventSubsystem.Serialization.Implementations.MsgPackResolvers;
4 | using FxEvents.Shared.Exceptions;
5 | using FxEvents.Shared.Serialization.Implementations.MsgPackResolvers;
6 | using FxEvents.Shared.TypeExtensions;
7 | using Logger;
8 | using MsgPack;
9 | using MsgPack.Serialization;
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Linq;
13 | using System.Reflection;
14 | using System.Text;
15 |
16 | namespace FxEvents.Shared.Serialization.Implementations
17 | {
18 | public class MsgPackSerialization : ISerialization
19 | {
20 | private Log logger = new();
21 | private MsgPack.Serialization.SerializationContext _context = new(MsgPack.PackerCompatibilityOptions.None) { SerializationMethod = SerializationMethod.Map, GeneratorOption = SerializationMethodGeneratorOption.Fast };
22 | public MsgPackSerialization()
23 | {
24 | Vector2Resolver vector2 = new(_context);
25 | Vector3Resolver vector3 = new(_context);
26 | Vector4Resolver vector4 = new(_context);
27 | QuaternionResolver quaternion = new(_context);
28 | MatrixResolver matrix = new(_context);
29 | Matrix3x3Resolver matrix3x3 = new(_context);
30 | SnowflakeResolver snowflake = new(_context);
31 | PlayerResolver player = new(_context);
32 | EntityResolver entity = new(_context);
33 | PedResolver ped = new(_context);
34 | PropResolver prop = new(_context);
35 | VehicleResolver vehicle = new(_context);
36 | DoubleFixer doubleFixer = new(_context);
37 |
38 | _context.Serializers.RegisterOverride(vector2);
39 | _context.Serializers.RegisterOverride(vector3);
40 | _context.Serializers.RegisterOverride(vector4);
41 | _context.Serializers.RegisterOverride(quaternion);
42 | _context.Serializers.RegisterOverride(matrix);
43 | _context.Serializers.RegisterOverride(matrix3x3);
44 | _context.Serializers.RegisterOverride(snowflake);
45 | _context.Serializers.RegisterOverride(player);
46 | _context.Serializers.RegisterOverride(entity);
47 | _context.Serializers.RegisterOverride(ped);
48 | _context.Serializers.RegisterOverride(prop);
49 | _context.Serializers.RegisterOverride(vehicle);
50 | _context.Serializers.RegisterOverride(doubleFixer);
51 | }
52 |
53 | private bool CanCreateInstanceUsingDefaultConstructor(Type t) => t.IsValueType || !t.IsAbstract && t.GetConstructor(Type.EmptyTypes) != null;
54 | private bool IsTuple(Type t) => t.Name.StartsWith("Tuple");
55 | private bool ContainsTuple(Type t) => t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).Any(x=>x.Name.StartsWith("Tuple"))||
56 | t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).Any(x => x.Name.StartsWith("Tuple"));
57 | private Type[] GetTupleTypes(Type t)
58 | {
59 | List types = [];
60 | var fields = t.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).Where(x => x.Name.StartsWith("Tuple"));
61 | var properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static).Where(x => x.Name.StartsWith("Tuple"));
62 |
63 |
64 | return types.ToArray();
65 | }
66 | private static string GetTypeIdentifier(Type type)
67 | {
68 | StringBuilder builder = new StringBuilder();
69 | Type declaring = type;
70 |
71 | builder.Append(type.Namespace);
72 | builder.Append(".");
73 |
74 | int idx = builder.Length;
75 |
76 | while ((declaring = declaring.DeclaringType) != null)
77 | {
78 | builder.Insert(idx, declaring.Name + ".");
79 | }
80 |
81 | builder.Append(type.Name);
82 |
83 | return builder.ToString();
84 | }
85 |
86 |
87 | #region Serialization
88 | public void Serialize(Type type, object value, SerializationContext context)
89 | {
90 | if (IsTuple(type))
91 | {
92 | logger.Warning("Using Tuple is not advised due to differences between client and server environments and the unavailability of resolvers. Consider using ValueTuple instead.");
93 | SerializeTuple(type, value, context);
94 | }
95 | SerializeObject(type, value, context);
96 | }
97 |
98 | private void SerializeTuple(Type type, object value, SerializationContext context)
99 | {
100 | PropertyInfo[] properties = value.GetType().GetProperties();
101 |
102 | foreach (PropertyInfo property in properties)
103 | {
104 | object propertyValue = property.GetValue(value, null);
105 | Serialize(propertyValue, context);
106 | }
107 | }
108 |
109 | private void SerializeObject(Type type, object value, SerializationContext context)
110 | {
111 | IMessagePackSingleObjectSerializer ser = MessagePackSerializer.Get(type, _context);
112 | ser.Pack(context.Writer.BaseStream, value);
113 | }
114 |
115 | public void Serialize(T value, SerializationContext context)
116 | {
117 | Serialize(typeof(T), value, context);
118 | }
119 | #endregion
120 |
121 | #region Deserialization
122 | public object Deserialize(Type type, SerializationContext context)
123 | {
124 | IMessagePackSingleObjectSerializer ser = MessagePackSerializer.Get(type, _context);
125 | object @return = ser.Unpack(context.Reader.BaseStream);
126 | return @return;
127 | }
128 |
129 | public T Deserialize(SerializationContext context) => Deserialize(typeof(T), context);
130 |
131 | public T Deserialize(Type type, SerializationContext context)
132 | {
133 | if (IsTuple(type))
134 | {
135 | logger.Warning("Using Tuple is not advised due to differences between client and server environments and the unavailability of resolvers. Consider using ValueTuple instead.");
136 | return DeserializeTuple(type, context);
137 | }
138 | return DeserializeObject(type, context);
139 | }
140 |
141 | private T DeserializeTuple(Type type, SerializationContext context)
142 | {
143 |
144 | Type[] generics = type.GetGenericArguments();
145 | System.Reflection.ConstructorInfo constructor = type.GetConstructor(generics) ??
146 | throw new SerializationException(context, type,
147 | $"Could not find suitable constructor for type: {type.Name}");
148 | List