├── .gitattributes ├── .gitignore ├── .vscode └── settings.json ├── Assets ├── .gitKeep ├── Logo │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-125.png │ ├── Square150x150Logo.scale-150.png │ ├── Square150x150Logo.scale-200.png │ ├── Square150x150Logo.scale-400.png │ ├── Square310x310Logo.scale-100.png │ ├── Square310x310Logo.scale-125.png │ ├── Square310x310Logo.scale-150.png │ ├── Square310x310Logo.scale-200.png │ ├── Square310x310Logo.scale-400.png │ ├── Square44x44Logo.targetsize-16.png │ ├── Square44x44Logo.targetsize-20.png │ ├── Square44x44Logo.targetsize-24.png │ ├── Square44x44Logo.targetsize-256.png │ ├── Square44x44Logo.targetsize-30.png │ ├── Square44x44Logo.targetsize-32.png │ ├── Square44x44Logo.targetsize-36.png │ ├── Square44x44Logo.targetsize-40.png │ ├── Square44x44Logo.targetsize-48.png │ ├── Square44x44Logo.targetsize-60.png │ ├── Square44x44Logo.targetsize-64.png │ ├── Square44x44Logo.targetsize-72.png │ ├── Square44x44Logo.targetsize-80.png │ ├── Square44x44Logo.targetsize-96.png │ ├── Square71x71Logo.scale-100.png │ ├── Square71x71Logo.scale-125.png │ ├── Square71x71Logo.scale-150.png │ ├── Square71x71Logo.scale-200.png │ ├── Square71x71Logo.scale-400.png │ ├── StoreLogo.scale-150.png │ ├── StoreLogo.scale-300.png │ ├── StoreLogo.scale-71.png │ ├── icon.ico │ ├── square150x150logo_scale_100.ico │ ├── square150x150logo_scale_125.ico │ ├── square150x150logo_scale_200_256px.ico │ ├── square150x150logo_scale_400_256px.ico │ ├── square310x310logo_scale_100_256px.ico │ ├── square310x310logo_scale_150_256px.ico │ ├── square310x310logo_scale_200_256px.ico │ ├── square310x310logo_scale_400_256px.ico │ ├── square44x44logo_targetsize_256.ico │ ├── square44x44logo_targetsize_80.ico │ ├── square44x44logo_targetsize_96.ico │ ├── square71x71logo_scale_100.ico │ ├── square71x71logo_scale_125.ico │ ├── square71x71logo_scale_150.ico │ ├── square71x71logo_scale_200.ico │ ├── square71x71logo_scale_400_256px.ico │ ├── storelogo_scale_150.ico │ ├── storelogo_scale_300_256px.ico │ └── storelogo_scale_71.ico └── rr.txt ├── CONTRIBUTING.md ├── LICENSE ├── Localization └── English │ └── MP_UI.tyd ├── Multiplayer.Core ├── AdditionalOutputArtifacts │ └── steam_api64_new.dll ├── Behaviours │ ├── MultiplayerMenuBehaviour.cs │ ├── NetworkingClientBehaviour.cs │ ├── NetworkingServerBehaviour.cs │ └── SteamHelperBehaviour.cs ├── DebugConsole.cs ├── Meta.cs ├── Multiplayer - Backup.Core.csproj ├── Multiplayer.Core.csproj ├── Multiplayer.Core.csproj.DotSettings ├── Multiplayer.Core.csproj.bak ├── README.md ├── Utils │ ├── IPUtils.cs │ ├── SettingsHandler.cs │ └── UItils.cs └── icon.ico ├── Multiplayer.Debugging ├── FileLogger.cs ├── Multiplayer.Debugging.csproj └── UnityLogger.cs ├── Multiplayer.Extensions ├── AudioHandler.cs ├── Multiplayer.Extensions.csproj ├── Path.cs ├── StringExt.cs └── UI.cs ├── Multiplayer.Networking.Test ├── Multiplayer.Networking.Test.csproj ├── ServerHandlerTest.cs ├── ServerSidedTests.cs ├── SteamWorksTests.cs └── TestLogger.cs ├── Multiplayer.Networking ├── Client │ ├── ClientEvents.cs │ ├── GameClient.cs │ ├── GameClientSocket.cs │ ├── GameClient_old.cs │ └── Handlers │ │ ├── ChatHandler.cs │ │ └── ClientPacketHandler.cs ├── Constants.cs ├── Multiplayer.Networking.csproj ├── Packets │ ├── AuthResponse.cs │ ├── ChatMessage.cs │ ├── GamePacket.cs │ ├── Handshake.cs │ ├── Moderation.cs │ ├── generatePackets.ps1 │ └── proto │ │ ├── authResponse.proto │ │ ├── chatMessage.proto │ │ ├── gamePacket.proto │ │ ├── handshake.proto │ │ └── moderation.proto ├── SIMM.cs ├── Server │ ├── GameServer.cs │ ├── GameServerSocket.cs │ ├── GameServer_old.cs │ ├── Handlers │ │ ├── ChatHandler.cs │ │ ├── HandshakeHandler.cs │ │ └── ServerPacketHandler.cs │ ├── Managers │ │ └── BanManager.cs │ ├── ServerEvents.cs │ └── ServerInfo.cs ├── Shared │ ├── GameUser.cs │ ├── IPacketHandler.cs │ └── Managers │ │ ├── RegisterManager.cs │ │ └── UserManager.cs └── Utility │ ├── PacketSerializer.cs │ └── SteamHelper.cs ├── Multiplayer.Shared ├── ILogger.cs ├── Multiplayer.Shared.csproj └── Multiplayer.Shared.csproj.bak ├── README.md ├── Utilities └── setSoftwareIncFolder.bat ├── create-binaries.ps1 ├── lib ├── net46 │ ├── Facepunch.Steamworks.Win64.dll │ ├── Facepunch.Steamworks.Win64.xml │ └── steam_api64_new.dll └── netstandard2.0 │ ├── Facepunch.Steamworks.Win64.deps.json │ ├── Facepunch.Steamworks.Win64.dll │ ├── Facepunch.Steamworks.Win64.xml │ └── steam_api64_new.dll └── swinc.multiplayer.sln /.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 | -------------------------------------------------------------------------------- /.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 | *.zip 9 | installer-binaries/ 10 | *.suo 11 | *.user 12 | *.userosscache 13 | *.sln.docstates 14 | out/ 15 | node_modules/ 16 | 17 | # User-specific files (MonoDevelop/Xamarin Studio) 18 | *.userprefs 19 | 20 | # Mono auto generated files 21 | mono_crash.* 22 | 23 | # Build results 24 | [Dd]ebug/ 25 | [Dd]ebugPublic/ 26 | [Rr]elease/ 27 | [Rr]eleases/ 28 | x64/ 29 | x86/ 30 | [Aa][Rr][Mm]/ 31 | [Aa][Rr][Mm]64/ 32 | bld/ 33 | [Bb]in/ 34 | [Oo]bj/ 35 | [Ll]og/ 36 | [Ll]ogs/ 37 | 38 | # Visual Studio 2015/2017 cache/options directory 39 | .vs/ 40 | .github/ 41 | # Uncomment if you have tasks that create the project's static files in wwwroot 42 | #wwwroot/ 43 | 44 | # IntelliJ Rider 2019-2022 Folder 45 | .idea/ 46 | 47 | # Visual Studio 2017 auto generated files 48 | Generated\ Files/ 49 | 50 | # MSTest test Results 51 | [Tt]est[Rr]esult*/ 52 | [Bb]uild[Ll]og.* 53 | 54 | # NUnit 55 | *.VisualState.xml 56 | TestResult.xml 57 | nunit-*.xml 58 | 59 | # Build Results of an ATL Project 60 | [Dd]ebugPS/ 61 | [Rr]eleasePS/ 62 | dlldata.c 63 | 64 | # Benchmark Results 65 | BenchmarkDotNet.Artifacts/ 66 | 67 | # .NET Core 68 | project.lock.json 69 | project.fragment.lock.json 70 | artifacts/ 71 | 72 | # StyleCop 73 | StyleCopReport.xml 74 | 75 | # Files built by Visual Studio 76 | *_i.c 77 | *_p.c 78 | *_h.h 79 | *.ilk 80 | *.meta 81 | *.obj 82 | *.iobj 83 | *.pch 84 | *.pdb 85 | *.ipdb 86 | *.pgc 87 | *.pgd 88 | *.rsp 89 | *.sbr 90 | *.tlb 91 | *.tli 92 | *.tlh 93 | *.tmp 94 | *.tmp_proj 95 | *_wpftmp.csproj 96 | *.log 97 | *.vspscc 98 | *.vssscc 99 | .builds 100 | *.pidb 101 | *.svclog 102 | *.scc 103 | 104 | # Chutzpah Test files 105 | _Chutzpah* 106 | 107 | # Visual C++ cache files 108 | ipch/ 109 | *.aps 110 | *.ncb 111 | *.opendb 112 | *.opensdf 113 | *.sdf 114 | *.cachefile 115 | *.VC.db 116 | *.VC.VC.opendb 117 | 118 | # Visual Studio profiler 119 | *.psess 120 | *.vsp 121 | *.vspx 122 | *.sap 123 | 124 | # Visual Studio Trace Files 125 | *.e2e 126 | 127 | # TFS 2012 Local Workspace 128 | $tf/ 129 | 130 | # Guidance Automation Toolkit 131 | *.gpState 132 | 133 | # ReSharper is a .NET coding add-in 134 | _ReSharper*/ 135 | *.[Rr]e[Ss]harper 136 | *.DotSettings.user 137 | 138 | # TeamCity is a build add-in 139 | _TeamCity* 140 | 141 | # DotCover is a Code Coverage Tool 142 | *.dotCover 143 | 144 | # AxoCover is a Code Coverage Tool 145 | .axoCover/* 146 | !.axoCover/settings.json 147 | 148 | # Visual Studio code coverage results 149 | *.coverage 150 | *.coveragexml 151 | 152 | # NCrunch 153 | _NCrunch_* 154 | .*crunch*.local.xml 155 | nCrunchTemp_* 156 | 157 | # MightyMoose 158 | *.mm.* 159 | AutoTest.Net/ 160 | 161 | # Web workbench (sass) 162 | .sass-cache/ 163 | 164 | # Installshield output folder 165 | [Ee]xpress/ 166 | 167 | # DocProject is a documentation generator add-in 168 | DocProject/buildhelp/ 169 | DocProject/Help/*.HxT 170 | DocProject/Help/*.HxC 171 | DocProject/Help/*.hhc 172 | DocProject/Help/*.hhk 173 | DocProject/Help/*.hhp 174 | DocProject/Help/Html2 175 | DocProject/Help/html 176 | 177 | # Click-Once directory 178 | publish/ 179 | 180 | # Publish Web Output 181 | *.[Pp]ublish.xml 182 | *.azurePubxml 183 | # Note: Comment the next line if you want to checkin your web deploy settings, 184 | # but database connection strings (with potential passwords) will be unencrypted 185 | *.pubxml 186 | *.publishproj 187 | 188 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 189 | # checkin your Azure Web App publish settings, but sensitive information contained 190 | # in these scripts will be unencrypted 191 | PublishScripts/ 192 | 193 | # NuGet Packages 194 | *.nupkg 195 | # NuGet Symbol Packages 196 | *.snupkg 197 | # The packages folder can be ignored because of Package Restore 198 | **/[Pp]ackages/* 199 | # except build/, which is used as an MSBuild target. 200 | !**/[Pp]ackages/build/ 201 | # Uncomment if necessary however generally it will be regenerated when needed 202 | #!**/[Pp]ackages/repositories.config 203 | # NuGet v3's project.json files produces more ignorable files 204 | *.nuget.props 205 | *.nuget.targets 206 | 207 | # Microsoft Azure Build Output 208 | csx/ 209 | *.build.csdef 210 | 211 | # Microsoft Azure Emulator 212 | ecf/ 213 | rcf/ 214 | 215 | # Windows Store app package directories and files 216 | AppPackages/ 217 | BundleArtifacts/ 218 | Package.StoreAssociation.xml 219 | _pkginfo.txt 220 | *.appx 221 | *.appxbundle 222 | *.appxupload 223 | 224 | # Visual Studio cache files 225 | # files ending in .cache can be ignored 226 | *.[Cc]ache 227 | # but keep track of directories ending in .cache 228 | !?*.[Cc]ache/ 229 | 230 | # Others 231 | ClientBin/ 232 | ~$* 233 | *~ 234 | *.dbmdl 235 | *.dbproj.schemaview 236 | *.jfm 237 | *.pfx 238 | *.publishsettings 239 | orleans.codegen.cs 240 | 241 | # Including strong name files can present a security risk 242 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 243 | #*.snk 244 | 245 | # Since there are multiple workflows, uncomment next line to ignore bower_components 246 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 247 | #bower_components/ 248 | 249 | # RIA/Silverlight projects 250 | Generated_Code/ 251 | 252 | # Backup & report files from converting an old project file 253 | # to a newer Visual Studio version. Backup files are not needed, 254 | # because we have git ;-) 255 | _UpgradeReport_Files/ 256 | Backup*/ 257 | UpgradeLog*.XML 258 | UpgradeLog*.htm 259 | ServiceFabricBackup/ 260 | *.rptproj.bak 261 | 262 | # SQL Server files 263 | *.mdf 264 | *.ldf 265 | *.ndf 266 | 267 | # Business Intelligence projects 268 | *.rdl.data 269 | *.bim.layout 270 | *.bim_*.settings 271 | *.rptproj.rsuser 272 | *- [Bb]ackup.rdl 273 | *- [Bb]ackup ([0-9]).rdl 274 | *- [Bb]ackup ([0-9][0-9]).rdl 275 | 276 | # Microsoft Fakes 277 | FakesAssemblies/ 278 | 279 | # GhostDoc plugin setting file 280 | *.GhostDoc.xml 281 | 282 | # Node.js Tools for Visual Studio 283 | .ntvs_analysis.dat 284 | node_modules/ 285 | 286 | # Visual Studio 6 build log 287 | *.plg 288 | 289 | # Visual Studio 6 workspace options file 290 | *.opt 291 | 292 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 293 | *.vbw 294 | 295 | # Visual Studio LightSwitch build output 296 | **/*.HTMLClient/GeneratedArtifacts 297 | **/*.DesktopClient/GeneratedArtifacts 298 | **/*.DesktopClient/ModelManifest.xml 299 | **/*.Server/GeneratedArtifacts 300 | **/*.Server/ModelManifest.xml 301 | _Pvt_Extensions 302 | 303 | # Paket dependency manager 304 | .paket/paket.exe 305 | paket-files/ 306 | 307 | # FAKE - F# Make 308 | .fake/ 309 | 310 | # CodeRush personal settings 311 | .cr/personal 312 | 313 | # Python Tools for Visual Studio (PTVS) 314 | __pycache__/ 315 | *.pyc 316 | 317 | # Cake - Uncomment if you are using it 318 | # tools/** 319 | # !tools/packages.config 320 | 321 | # Tabs Studio 322 | *.tss 323 | 324 | # Telerik's JustMock configuration file 325 | *.jmconfig 326 | 327 | # BizTalk build output 328 | *.btp.cs 329 | *.btm.cs 330 | *.odx.cs 331 | *.xsd.cs 332 | 333 | # OpenCover UI analysis results 334 | OpenCover/ 335 | 336 | # Azure Stream Analytics local run output 337 | ASALocalRun/ 338 | 339 | # MSBuild Binary and Structured Log 340 | *.binlog 341 | 342 | # NVidia Nsight GPU debugger configuration file 343 | *.nvuser 344 | 345 | # MFractors (Xamarin productivity tool) working folder 346 | .mfractor/ 347 | 348 | # Local History for Visual Studio 349 | .localhistory/ 350 | 351 | # BeatPulse healthcheck temp database 352 | healthchecksdb 353 | 354 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 355 | MigrationBackup/ 356 | 357 | # Ionide (cross platform F# VS Code tools) working folder 358 | .ionide/ 359 | /swinc.multiplayer/Assets/mainMenuButton.png 360 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /Assets/.gitKeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/.gitKeep -------------------------------------------------------------------------------- /Assets/Logo/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Logo/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Logo/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Logo/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Logo/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Logo/Square310x310Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square310x310Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Logo/Square310x310Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square310x310Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Logo/Square310x310Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square310x310Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Logo/Square310x310Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square310x310Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Logo/Square310x310Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square310x310Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-20.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-30.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-36.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-40.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-60.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-64.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-72.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-80.png -------------------------------------------------------------------------------- /Assets/Logo/Square44x44Logo.targetsize-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square44x44Logo.targetsize-96.png -------------------------------------------------------------------------------- /Assets/Logo/Square71x71Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square71x71Logo.scale-100.png -------------------------------------------------------------------------------- /Assets/Logo/Square71x71Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square71x71Logo.scale-125.png -------------------------------------------------------------------------------- /Assets/Logo/Square71x71Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square71x71Logo.scale-150.png -------------------------------------------------------------------------------- /Assets/Logo/Square71x71Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square71x71Logo.scale-200.png -------------------------------------------------------------------------------- /Assets/Logo/Square71x71Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/Square71x71Logo.scale-400.png -------------------------------------------------------------------------------- /Assets/Logo/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /Assets/Logo/StoreLogo.scale-300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/StoreLogo.scale-300.png -------------------------------------------------------------------------------- /Assets/Logo/StoreLogo.scale-71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/StoreLogo.scale-71.png -------------------------------------------------------------------------------- /Assets/Logo/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/icon.ico -------------------------------------------------------------------------------- /Assets/Logo/square150x150logo_scale_100.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square150x150logo_scale_100.ico -------------------------------------------------------------------------------- /Assets/Logo/square150x150logo_scale_125.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square150x150logo_scale_125.ico -------------------------------------------------------------------------------- /Assets/Logo/square150x150logo_scale_200_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square150x150logo_scale_200_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square150x150logo_scale_400_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square150x150logo_scale_400_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square310x310logo_scale_100_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square310x310logo_scale_100_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square310x310logo_scale_150_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square310x310logo_scale_150_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square310x310logo_scale_200_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square310x310logo_scale_200_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square310x310logo_scale_400_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square310x310logo_scale_400_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/square44x44logo_targetsize_256.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square44x44logo_targetsize_256.ico -------------------------------------------------------------------------------- /Assets/Logo/square44x44logo_targetsize_80.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square44x44logo_targetsize_80.ico -------------------------------------------------------------------------------- /Assets/Logo/square44x44logo_targetsize_96.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square44x44logo_targetsize_96.ico -------------------------------------------------------------------------------- /Assets/Logo/square71x71logo_scale_100.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square71x71logo_scale_100.ico -------------------------------------------------------------------------------- /Assets/Logo/square71x71logo_scale_125.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square71x71logo_scale_125.ico -------------------------------------------------------------------------------- /Assets/Logo/square71x71logo_scale_150.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square71x71logo_scale_150.ico -------------------------------------------------------------------------------- /Assets/Logo/square71x71logo_scale_200.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square71x71logo_scale_200.ico -------------------------------------------------------------------------------- /Assets/Logo/square71x71logo_scale_400_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/square71x71logo_scale_400_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/storelogo_scale_150.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/storelogo_scale_150.ico -------------------------------------------------------------------------------- /Assets/Logo/storelogo_scale_300_256px.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/storelogo_scale_300_256px.ico -------------------------------------------------------------------------------- /Assets/Logo/storelogo_scale_71.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Assets/Logo/storelogo_scale_71.ico -------------------------------------------------------------------------------- /Assets/rr.txt: -------------------------------------------------------------------------------- 1 | We're no strangers to love 2 | You know the rules and so do I 3 | A full commitment's what I'm thinking of 4 | You wouldn't get this from any other guy 5 | 6 | I just wanna tell you how I'm feeling 7 | Gotta make you understand 8 | 9 | Never gonna give you up 10 | Never gonna let you down 11 | Never gonna run around and desert you 12 | Never gonna make you cry 13 | Never gonna say goodbye 14 | Never gonna tell a lie and hurt you 15 | 16 | We've known each other for so long 17 | Your heart's been aching, but 18 | You're too shy to say it 19 | Inside, we both know what's been going on 20 | We know the game and we're gonna play it 21 | 22 | And if you ask me how I'm feeling 23 | Don't tell me you're too blind to see 24 | 25 | Never gonna give you up 26 | Never gonna let you down 27 | Never gonna run around and desert you 28 | Never gonna make you cry 29 | Never gonna say goodbye 30 | Never gonna tell a lie and hurt you 31 | 32 | Never gonna give you up 33 | Never gonna let you down 34 | Never gonna run around and desert you 35 | Never gonna make you cry 36 | Never gonna say goodbye 37 | Never gonna tell a lie and hurt you 38 | 39 | (Ooh, give you up) 40 | (Ooh, give you up) 41 | Never gonna give, never gonna give 42 | (Give you up) 43 | Never gonna give, never gonna give 44 | (Give you up) 45 | 46 | We've known each other for so long 47 | Your heart's been aching, but 48 | You're too shy to say it 49 | Inside, we both know what's been going on 50 | We know the game and we're gonna play it 51 | 52 | I just wanna tell you how I'm feeling 53 | Gotta make you understand 54 | 55 | Never gonna give you up 56 | Never gonna let you down 57 | Never gonna run around and desert you 58 | Never gonna make you cry 59 | Never gonna say goodbye 60 | Never gonna tell a lie and hurt you 61 | 62 | Never gonna give you up 63 | Never gonna let you down 64 | Never gonna run around and desert you 65 | Never gonna make you cry 66 | Never gonna say goodbye 67 | Never gonna tell a lie and hurt you 68 | 69 | Never gonna give you up 70 | Never gonna let you down 71 | Never gonna run around and desert you 72 | Never gonna make you cry 73 | Never gonna say goodbye 74 | Never gonna tell a lie and hurt you -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Software Inc Multiplayer contributing guidelines. 2 | 3 | These guidelines must be followed or your Pull requests or other methods of contributing will be denied. 4 | 5 | #### Code Guidelines. 6 | 7 | - You **MUST** remove all errors from your code, test and check if your contribution works as intended 8 | - If modifying code in `Multiplayer.Networking`, you **MUST** state in high detail what you have changed to keep privacy. 9 | - When contributing you **MUST NOT** put "X was here" or "X made this" in any way or format, you will be listed on the contributors list. This is to stop the code from becoming messy and unreadable. 10 | - Any text players/users of the mod will see must be localized in english in the correct Localization file. 11 | 12 | #### Translation Guidelines 13 | 14 | - You **MUST NOT** use google translate or any online translator unless necessary. 15 | - Your translations must be correct in grammar, formatting and keep close to the original translation. 16 | 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 daRedLoCo and CyaCal 2 | 3 | Licensed under the OE-O Open Source License, Version 0.4 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | https://oe-o.tk/legal-docs/oe-o-license.txt 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Localization/English/MP_UI.tyd: -------------------------------------------------------------------------------- 1 | FirstTimeWindowTitle "First time? Neat!" 2 | FirstTimeWindow_OpenWiki "Open Wiki" 3 | FirstTimeWindowDescription "Thanks for participating in the Open Beta%steamusername%!\nCheckout the wiki on how to start a server, join or play with friends!" 4 | FirstTimeWindowDescription2 "The wiki also contains useful info on how to configure your server, set passwords on your server, and lots more! It also contains some useful infomation on how to debug/fix problems related to multiplayer." 5 | FirstTimeWindow_OK "Ok" 6 | MultiplayerButton "Multiplayer" 7 | MultiplayerButtonConnect "Multiplayer - Connect" 8 | ConnectButtonText "Connect" 9 | GoBackButton "Go Back" 10 | GoBackTooltip "Go back to the main multiplayer window." 11 | CancelButton "Cancel" 12 | ServerButtonText "Create Server" 13 | ConnectLabel "Connect to a multiplayer server via IP and Port, specify a password if the server has a password set." 14 | NoIPText "Please enter a IP into the text box labeled \"Server IP\"" 15 | NoPortText "Please enter a valid Port into the text box labeled \"Port\"" 16 | NoIPText "Please enter a IP into the text box labeled \"Server IP\"" 17 | PortInput "Port" 18 | PasswordInput "Password" 19 | AlreadyConnectedToServer "You are already connected to a server, would you like to disconnect?" 20 | NotConnectedToServer "You aren't connected to a server!" 21 | TypeToChat "Type here to chat..." 22 | NoMessages "Its pretty quiet in here, seems to be no sign of chat messages anywhere!" 23 | ComingSoon "Coming soon." 24 | SuccessfullyConnected "Successfully connected to the server!" 25 | Yes "Yes" 26 | No "No" 27 | UsersButtonText "Manage Users" 28 | StartButtonText "Start" 29 | SuccessfullyCreated "Successfully created server!" 30 | StopButtonText "Stop Server" 31 | AlreadyServer "You already have a server started, would you like to stop it?" 32 | InvalidPort "Please enter a valid port into the box labeled \"Port\"" 33 | MultiplayerButtonSS "Multiplayer - Create Server" 34 | CreateLabel "Create a multiplayer server on the specified port, leave password blank to disable password verification." 35 | CreateServerTab "Create Server" 36 | ConnectServerTab "Connect to Server" -------------------------------------------------------------------------------- /Multiplayer.Core/AdditionalOutputArtifacts/steam_api64_new.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Multiplayer.Core/AdditionalOutputArtifacts/steam_api64_new.dll -------------------------------------------------------------------------------- /Multiplayer.Core/Behaviours/NetworkingClientBehaviour.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Multiplayer.Debugging; 4 | using Multiplayer.Networking.Client; 5 | using Multiplayer.Networking.Client.Handlers; 6 | using Multiplayer.Networking.Shared; 7 | using UnityEngine; 8 | 9 | namespace Multiplayer.Core.Behaviours 10 | { 11 | 12 | [DisallowMultipleComponent] 13 | public class NetworkingClientBehaviour : ModBehaviour, IDisposable 14 | 15 | { 16 | private Shared.ILogger log; 17 | 18 | //public GameClient_old Client { get; private set; } 19 | public GameClient Client { get; private set; } 20 | public ChatHandler ChatHandler { get; private set; } 21 | public IUserManager UserManager { get; private set; } 22 | public bool IsConnected = false; 23 | 24 | 25 | public override void OnActivate() 26 | { 27 | Meta.NetworkingClient = this; 28 | this.log = new UnityLogger(); 29 | this.log.Debug("client behavior booting"); 30 | 31 | //if (!SteamManager.Initialized) 32 | // return; 33 | 34 | //var currentUserId = Steamworks.SteamUser.GetSteamID().m_SteamID; 35 | //var currentUserName = Steamworks.SteamFriends.GetPersonaName(); 36 | //this.log.Debug("got steam info", currentUserId, currentUserName); 37 | 38 | /*var currentUser = new GameUser() 39 | { 40 | Id = currentUserId, 41 | Name = currentUserName, 42 | Role = UserRole.Guest 43 | }; 44 | 45 | this.UserManager = new UserManager(); 46 | 47 | this.Client = new GameClient_old( 48 | this.log, 49 | currentUser, 50 | new PacketSerializer(), 51 | this.UserManager 52 | ); 53 | 54 | this.RegisterPacketHandler();*/ 55 | 56 | this.Client = new GameClient(log, null); 57 | Client.ConnectionStateChange += (sender, b) => this.IsConnected = b; 58 | this.log.Debug("client behaviour booted"); 59 | } 60 | 61 | private void RegisterPacketHandler() 62 | { 63 | //this.ChatHandler = new ChatHandler(this.Client); 64 | //this.Client.RegisterPacketHandler(this.ChatHandler); 65 | } 66 | 67 | public override void OnDeactivate() 68 | { 69 | this.log.Debug("destroying client behaviour"); 70 | Dispose(); 71 | } 72 | 73 | [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Unity")] 74 | private void Update() 75 | { 76 | 77 | // this is the games update loop 78 | //this.Client.HandleMessages(); 79 | } 80 | 81 | public void Connect(string host, int port) 82 | { 83 | this.log.Debug($"[client] connecting to {host}:{port}"); 84 | try 85 | { 86 | 87 | this.Client.Connect(host, (ushort)port); 88 | } 89 | catch (Exception ex) 90 | { 91 | this.log.Error("[client] not connected", ex); 92 | } 93 | 94 | this.log.Debug("[client] connected"); 95 | } 96 | 97 | public void Disconnect() 98 | { 99 | log.Debug("[client] disconnecting.."); 100 | 101 | try 102 | { 103 | Client.Disconnect(); 104 | } 105 | catch (Exception e) 106 | { 107 | this.log.Error("[client] not connected", e); 108 | } 109 | } 110 | 111 | public void Dispose() 112 | { 113 | try 114 | { 115 | Client?.Dispose(); 116 | } 117 | catch (Exception ex) 118 | { 119 | log.Error(ex); 120 | } 121 | } 122 | 123 | 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /Multiplayer.Core/Behaviours/NetworkingServerBehaviour.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Multiplayer.Debugging; 4 | using Multiplayer.Networking; 5 | using Multiplayer.Networking.Server; 6 | using Multiplayer.Networking.Server.Handlers; 7 | using Multiplayer.Networking.Server.Managers; 8 | using Multiplayer.Networking.Shared; 9 | using UnityEngine; 10 | 11 | namespace Multiplayer.Core.Behaviours 12 | { 13 | 14 | [DisallowMultipleComponent] 15 | public class NetworkingServerBehaviour : ModBehaviour, IDisposable 16 | { 17 | private Shared.ILogger logger; 18 | //public GameServer_old Server { get; private set; } 19 | public GameServer Server { get; private set; } 20 | 21 | public IUserManager UserManager { get; private set; } 22 | public BanManager BanManager { get; private set; } 23 | public ChatHandler ChatHandler { get; private set; } 24 | 25 | public override void OnActivate() 26 | { 27 | Meta.NetworkingServer = this; 28 | this.logger = new UnityLogger(); 29 | this.logger.Debug("server behaviour booting"); 30 | 31 | this.UserManager = new UserManager(); 32 | this.BanManager = new BanManager(); 33 | 34 | /*this.Server = new GameServer_old( 35 | this.logger, 36 | new PacketSerializer(), 37 | this.UserManager, 38 | this.BanManager 39 | ); 40 | 41 | this.RegisterHandlers();*/ 42 | 43 | this.logger.Debug("server behaviour booted"); 44 | } 45 | 46 | private void RegisterHandlers() 47 | { 48 | //this.ChatHandler = new ChatHandler(this.Server); 49 | //this.Server.RegisterPacketHandler(this.ChatHandler); 50 | } 51 | 52 | public override void OnDeactivate() 53 | { 54 | this.logger.Debug("destroying server behaviour"); 55 | Dispose(); 56 | } 57 | 58 | public void Host(string name, string description, ushort port) 59 | { 60 | var serverInfo = new ServerInfo() 61 | { 62 | Name = name, 63 | Description = description, 64 | Port = port, 65 | DefaultRole = UserRole.Guest 66 | }; 67 | this.logger.Debug("[server] starting"); 68 | try 69 | { 70 | this.Server = new GameServer(logger); 71 | this.Server.Start(serverInfo); 72 | } 73 | catch (Exception ex) 74 | { 75 | this.logger.Error("[server] not started", ex); 76 | } 77 | 78 | Server.ClientConnected += (sender, args) => this.logger.Info("Client connected"); 79 | Server.ClientDisconnected += (sender, args) => this.logger.Info("Client disconnected"); 80 | Server.ServerStarted += (sender, args) => this.logger.Info("Server started"); 81 | Server.ServerStopped += (sender, args) => this.logger.Info("Server stopped"); 82 | 83 | this.logger.Debug("[server] started"); 84 | } 85 | 86 | [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Unity")] 87 | private void Update() 88 | { 89 | // this is the games update loop 90 | // this.Server.HandleMessages(); 91 | } 92 | 93 | public void Dispose() 94 | { 95 | Server?.Dispose(); 96 | Server = null; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Multiplayer.Core/Behaviours/SteamHelperBehaviour.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Facepunch.Steamworks; 4 | using Multiplayer.Debugging; 5 | using UnityEngine; 6 | using ILogger = Multiplayer.Shared.ILogger; 7 | 8 | namespace Multiplayer.Core.Behaviours 9 | { 10 | [DisallowMultipleComponent] 11 | public class SteamHelperBehaviour : ModBehaviour, IDisposable 12 | { 13 | private ILogger Logger; 14 | public static bool Initialized { get; private set; } = false; 15 | 16 | private const int Appid = 362620; 17 | 18 | public void OnDisable() 19 | { 20 | //Proper place to shutdown SteamClient in order to avoid hanging of the game when closing it 21 | OnDeactivate(); 22 | } 23 | 24 | public void OnEnable() 25 | { 26 | Logger = new UnityLogger(); 27 | OnActivate(); 28 | } 29 | 30 | public override void OnActivate() 31 | { 32 | if (Initialized) 33 | return; 34 | 35 | Meta.SteamHelper = this; 36 | this.Logger.Info("[STEAM] booting"); 37 | try 38 | { 39 | SteamClient.Init(Appid); 40 | Initialized = true; 41 | 42 | this.Logger.Info("[STEAM] booted"); 43 | 44 | this.Logger.Debug($"[STEAM] Name: {SteamClient.Name}"); 45 | this.Logger.Debug($"[STEAM] AccountId: {SteamClient.SteamId.AccountId}"); 46 | this.Logger.Debug($"[STEAM] IsValid: {SteamClient.IsValid}"); 47 | this.Logger.Debug($"[STEAM] IsLoggedOn: {SteamClient.IsLoggedOn}"); 48 | } 49 | catch (Exception ex) 50 | { 51 | this.Logger.Error("[STEAM] boot failed", ex); 52 | } 53 | } 54 | 55 | public override void OnDeactivate() 56 | { 57 | if (!Initialized) 58 | return; 59 | 60 | this.Logger.Info("[STEAM] shutdown"); 61 | try 62 | { 63 | SteamClient.Shutdown(); 64 | Initialized = false; 65 | 66 | this.Logger.Info("[STEAM] closed"); 67 | } 68 | catch (Exception ex) 69 | { 70 | this.Logger.Error("[STEAM] shutdown failed", ex); 71 | } 72 | } 73 | 74 | [SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Unity")] 75 | private void Update() 76 | { 77 | if (!Initialized) 78 | return; 79 | SteamClient.RunCallbacks(); 80 | } 81 | 82 | public void Dispose() => OnDeactivate(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Multiplayer.Core/DebugConsole.cs: -------------------------------------------------------------------------------- 1 | using Multiplayer.Debugging; 2 | using Multiplayer.Networking; 3 | using System; 4 | using System.Collections; 5 | using System.IO; 6 | using UnityEngine; 7 | using UnityEngine.SceneManagement; 8 | 9 | namespace Multiplayer.Core 10 | { 11 | /**internal class DebugConsole : ModBehaviour 12 | { 13 | public static DebugConsole instance; 14 | private bool inmain = false; 15 | public bool Rrinuse { get; set; } 16 | public void PlayRR() 17 | { 18 | if (Rrinuse) return; 19 | Rrinuse = true; 20 | string[] lines = File.ReadAllLines(Path.Combine(Meta.ThisMod.ModPath, "Assets", "rr.txt")); 21 | StartCoroutine(ReadLines(lines)); 22 | } 23 | 24 | public IEnumerator ReadLines(string[] lines) 25 | { 26 | foreach (string line in lines) 27 | { 28 | Meta.Logging.Info(line); 29 | yield return new WaitForSeconds(0.95f); 30 | } 31 | Rrinuse = false; 32 | } 33 | 34 | public override void OnActivate() 35 | { 36 | Rrinuse = false; 37 | SceneManager.sceneLoaded += OnSceneLoaded; 38 | Meta.Logging.Info("[DebugConsole] Adding console commands"); 39 | DevConsole.Command startservercmd = new DevConsole.Command("MULTIPLAYER_START", OnStartServer); 40 | DevConsole.Console.AddCommand(startservercmd); 41 | DevConsole.Command connectclientcmd = new DevConsole.Command("MULTIPLAYER_CONNECT", OnClientConnect); 42 | DevConsole.Console.AddCommand(connectclientcmd); 43 | DevConsole.Command sendchatcmd = new DevConsole.Command("MULTIPLAYER_CHAT", OnSendChat); 44 | DevConsole.Console.AddCommand(sendchatcmd); 45 | DevConsole.Command closeserver = new DevConsole.Command("MULTIPLAYER_STOP", OnServerStop); 46 | DevConsole.Console.AddCommand(closeserver); 47 | DevConsole.Command getuserlist = new DevConsole.Command("MULTIPLAYER_USERS", OnRequestUserList); 48 | DevConsole.Console.AddCommand(getuserlist); 49 | DevConsole.Command getgameworld = new DevConsole.Command("MULTIPLAYER_GAMEWORLD", OnRequestGameWorld); 50 | DevConsole.Console.AddCommand(getgameworld); 51 | DevConsole.Command easterEgg = new DevConsole.Command("SIMULATE_SALT", PlayRR); 52 | DevConsole.Console.AddCommand(easterEgg); 53 | DevConsole.Command savegameworld = new DevConsole.Command("MULTIPLAYER_SAVE", OnSaveGameWorld); 54 | DevConsole.Console.AddCommand(savegameworld); 55 | DevConsole.Command setgamespeed = new DevConsole.Command("MULTIPLAYER_SPEED", OnSetGameSpeed); 56 | DevConsole.Console.AddCommand(setgamespeed); 57 | DevConsole.Command pchatcommand = new DevConsole.Command("MULTIPLAYER_PCHAT",OnPrivateMessage); 58 | DevConsole.Console.AddCommand(pchatcommand); 59 | if (SceneManager.GetActiveScene().name == "MainScene") 60 | inmain = true; 61 | } 62 | 63 | public void OnSetGameSpeed(int speed) 64 | { 65 | if (speed < 0 || speed > 4) 66 | { 67 | Meta.Logging.Warn("[DebugConsole] Gamespeed can't be less than 0 or more than 4!"); 68 | return; 69 | } 70 | Meta.Logging.Info("[DebugConsole] Set gamespeed"); 71 | Client.Send(new Helpers.TcpGamespeed(speed, 0)); 72 | } 73 | 74 | public void OnSaveGameWorld() 75 | { 76 | if (!Networking.Server.Runs) 77 | { 78 | Meta.Logging.Warn("You need to have a Server running to use this command!"); 79 | return; 80 | } 81 | Networking.Server.Save(); 82 | } 83 | public void OnPrivateMessage(string username, string message) 84 | { 85 | if (!Networking.NetworkingManager.Client.RawClient.Connected) 86 | Meta.Logging.Warn("[DebugConsole] You need to be connected to a Server to use this command!"); 87 | var tmpUser = new Helpers.User(); 88 | tmpUser.Username = Client.Username; 89 | Client.Send(new Helpers.TcpPrivateChat(tmpUser, username, message)); 90 | } 91 | public void OnRequestGameWorld() 92 | { 93 | if (!Networking.NetworkingManager.Client.RawClient.Connected) 94 | Meta.Logging.Warn("[DebugConsole] You need to be connected to a Server to use this command!"); 95 | Networking.Client.Send(new Helpers.TcpGameWorld(Networking.GameWorld.Client.Instance.world, true)); 96 | } 97 | 98 | public void OnRequestUserList() 99 | { 100 | if (!Networking.NetworkingManager.Client.RawClient.Connected) 101 | Meta.Logging.Warn("[DebugConsole] You need to be connected to a Server to use this command!"); 102 | 103 | Networking.Client.Send(new Helpers.TcpRequest("userlist")); 104 | } 105 | 106 | public void OnServerStop() 107 | { 108 | try 109 | { 110 | Networking.Client.Disconnect(); 111 | Networking.Server.Stop(); 112 | } 113 | catch (Exception e) 114 | { 115 | Meta.Logging.Error(e.ToString()); 116 | } 117 | } 118 | 119 | public void OnSceneLoaded(Scene scene, LoadSceneMode mode) 120 | { 121 | if (scene.name == "MainScene") 122 | { 123 | inmain = true; 124 | } 125 | else 126 | { 127 | inmain = false; 128 | } 129 | } 130 | 131 | public void OnClientConnect(string ip, ushort port) 132 | { 133 | if (!inmain) 134 | { 135 | Meta.Logging.Warn("[DebugConsole] You can't use this command outside of the MainScene!"); 136 | return; 137 | } 138 | if (NetworkingManager.Client.RawClient.Connected) 139 | { 140 | Meta.Logging.Warn("[DebugConsole] You're already connected to a Server, please disconnect first!"); 141 | WindowManager.SpawnDialog("You're already connected to a Server, please disconnect first!", true, DialogWindow.DialogType.Warning); 142 | return; 143 | } 144 | //DEBUG 145 | if (ip == ".") 146 | ip = "127.0.0.1"; 147 | //DEBUG END 148 | try 149 | { 150 | NetworkingManager.Client.Connect(ip, port); 151 | } 152 | catch (Exception e) 153 | { 154 | Meta.Logging.Error(e.ToString()); 155 | } 156 | 157 | } 158 | 159 | public void OnSendChat(string arg0) 160 | { 161 | if (!inmain || !NetworkingManager.Client.RawClient.Connected) 162 | { 163 | Logging.Warn("[DebugConsole] You can't use this command outside of the MainScene!"); 164 | return; 165 | } 166 | var tmpUser = new Helpers.User(); 167 | tmpUser.Username = Client.Username; 168 | Helpers.TcpChat chatClass = new Helpers.TcpChat(arg0, tmpUser); 169 | Networking.Client.Send(chatClass); 170 | } 171 | 172 | public void OnStartServer(ushort port) 173 | { 174 | if (!inmain) 175 | { 176 | Logging.Warn("[DebugConsole] You can't use this command outside of the MainScene!"); 177 | return; 178 | } 179 | 180 | try 181 | { 182 | Networking.Server.Start(port); 183 | Networking.Client.Connect("127.0.0.1", port); 184 | } 185 | catch (Exception e) 186 | { 187 | Logging.Error(e.ToString()); 188 | } 189 | 190 | } 191 | 192 | public override void OnDeactivate() 193 | { 194 | Logging.Info("[DebugConsole] Removing console commands"); 195 | DevConsole.Console.RemoveCommand("MULTIPLAYER_START"); 196 | DevConsole.Console.RemoveCommand("MULTIPLAYER_CONNECT"); 197 | DevConsole.Console.RemoveCommand("MULTIPLAYER_CHAT"); 198 | DevConsole.Console.RemoveCommand("MULTIPLAYER_STOP"); 199 | DevConsole.Console.RemoveCommand("MULTIPLAYER_USERS"); 200 | DevConsole.Console.RemoveCommand("MULTIPLAYER_GAMEWORLD"); 201 | DevConsole.Console.RemoveCommand("SIMULATE_SALT"); 202 | DevConsole.Console.RemoveCommand("MULTIPLAYER_SAVE"); 203 | DevConsole.Console.RemoveCommand("MULTIPLAYER_SPEED"); 204 | DevConsole.Console.RemoveCommand("MULTIPLAYER_CHAT_CLEAR"); 205 | DevConsole.Console.RemoveCommand("MULTIPLAYER_PCHAT"); 206 | SceneManager.sceneLoaded -= OnSceneLoaded; 207 | } 208 | }**/ 209 | } 210 | -------------------------------------------------------------------------------- /Multiplayer.Core/Meta.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Multiplayer.Core.Behaviours; 3 | using Multiplayer.Debugging; 4 | using UnityEngine; 5 | using ILogger = Multiplayer.Shared.ILogger; 6 | //using Multiplayer.Extensions; 7 | 8 | namespace Multiplayer.Core 9 | { 10 | public class Meta : ModMeta, IDisposable 11 | { 12 | public static ILogger Logger { get; set; } 13 | public static ModController.DLLMod ThisMod { get; set; } 14 | 15 | public static bool GiveMeFreedom = true; 16 | 17 | public static NetworkingClientBehaviour NetworkingClient; 18 | public static NetworkingServerBehaviour NetworkingServer; 19 | public static SteamHelperBehaviour SteamHelper; 20 | public static MultiplayerMenuBehaviour MpWindow; 21 | 22 | public override string Name => "Software Inc Multiplayer"; 23 | public override void ConstructOptionsScreen(RectTransform parent, bool inGame) 24 | { 25 | // Button bthost = WindowManager.SpawnButton(); 26 | // bthost.onClick.AddListener(CreateBaseMultiplayerWindow); 27 | 28 | //bthost.SetText("Start"); 29 | // WindowManager.AddElementToElement(bthost.gameObject, parent.gameObject, new Rect(15, 15, 192, 64), Rect.zero); 30 | var label = WindowManager.SpawnLabel(); 31 | label.text = "Multiplayer Mod - Development"; 32 | WindowManager.AddElementToElement(label.gameObject, parent.gameObject, new Rect(15, 15, 192, 32), Rect.zero); 33 | } 34 | 35 | public override void Initialize(ModController.DLLMod parentMod) 36 | { 37 | Logger = new UnityLogger(); 38 | ThisMod = parentMod; 39 | 40 | Application.runInBackground = true; 41 | 42 | AppDomain.CurrentDomain.UnhandledException += (sender, args) => 43 | { 44 | Logger.Error("Error!" + ((Exception)args.ExceptionObject).StackTrace + "\n\n" + ((Exception)args.ExceptionObject).Message); 45 | }; 46 | 47 | base.Initialize(parentMod); 48 | } 49 | 50 | 51 | 52 | public void Dispose() 53 | { 54 | NetworkingClient?.Dispose(); 55 | NetworkingServer?.Dispose(); 56 | SteamHelper?.Dispose(); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Multiplayer.Core/Multiplayer - Backup.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ..\lib\net46\Facepunch.Steamworks.Win64.dll 17 | 18 | 19 | 20 | 21 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp.dll 22 | false 23 | 24 | 25 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp-firstpass.dll 26 | false 27 | 28 | 29 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.dll 30 | false 31 | 32 | 33 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.CoreModule.dll 34 | false 35 | 36 | 37 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.Networking.dll 38 | false 39 | 40 | 41 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.StandardEvents.dll 42 | false 43 | 44 | 45 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.TextRenderingModule.dll 46 | false 47 | 48 | 49 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UI.dll 50 | false 51 | 52 | 53 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIElementsModule.dll 54 | false 55 | 56 | 57 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIModule.dll 58 | false 59 | 60 | 61 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UNETModule.dll 62 | false 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /Multiplayer.Core/Multiplayer.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | full 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ..\lib\net46\Facepunch.Steamworks.Win64.dll 18 | 19 | 20 | 21 | 22 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp.dll 23 | false 24 | 25 | 26 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp-firstpass.dll 27 | false 28 | 29 | 30 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.dll 31 | false 32 | 33 | 34 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.CoreModule.dll 35 | false 36 | 37 | 38 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.Networking.dll 39 | false 40 | 41 | 42 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.StandardEvents.dll 43 | false 44 | 45 | 46 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.TextRenderingModule.dll 47 | false 48 | 49 | 50 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UI.dll 51 | false 52 | 53 | 54 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIElementsModule.dll 55 | false 56 | 57 | 58 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIModule.dll 59 | false 60 | 61 | 62 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UNETModule.dll 63 | false 64 | 65 | 66 | 67 | 68 | 69 | PreserveNewest 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /Multiplayer.Core/Multiplayer.Core.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | False -------------------------------------------------------------------------------- /Multiplayer.Core/README.md: -------------------------------------------------------------------------------- 1 | # Multiplayer.Core 2 | The core of the multiplayer mod. 3 | -------------------------------------------------------------------------------- /Multiplayer.Core/Utils/IPUtils.cs: -------------------------------------------------------------------------------- 1 | //using Newtonsoft.Json; 2 | using System.Net; 3 | using System.Text.RegularExpressions; 4 | using UnityEngine; 5 | namespace Multiplayer.Core 6 | { 7 | public static class IPUtils 8 | { 9 | public static string GetIP() 10 | { 11 | /*string externalIP; 12 | if (PlayerPrefs.HasKey("cachedIP")) 13 | { 14 | externalIP = (string)JsonConvert.DeserializeObject(PlayerPrefs.GetString("cachedIP")); 15 | return externalIP; 16 | } 17 | externalIP = (new WebClient()).DownloadString("http://checkip.dyndns.org/"); 18 | externalIP = (new Regex(@"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}")).Matches(externalIP)[0].ToString(); 19 | PlayerPrefs.SetString("cachedIP", JsonConvert.SerializeObject(externalIP)); 20 | return externalIP;*/ 21 | return ""; 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /Multiplayer.Core/Utils/SettingsHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | using UnityEngine; 6 | 7 | namespace Multiplayer.Core 8 | { 9 | public static class SettingsHandler 10 | { 11 | public static void Set(string key, T value) 12 | { 13 | XmlSerializer x = new XmlSerializer(value.GetType()); 14 | StringWriter writer = new StringWriter(); 15 | x.Serialize(writer, value); 16 | PlayerPrefs.SetString(key, writer.ToString()); 17 | } 18 | public static bool Has(string key) 19 | { 20 | return PlayerPrefs.HasKey(key); 21 | } 22 | public static bool TryGet(string key, Type type, out T result) 23 | { 24 | try 25 | { 26 | XmlSerializer x = new XmlSerializer(type); 27 | MemoryStream writer = new MemoryStream(); 28 | writer.Write(Encoding.UTF8.GetBytes(PlayerPrefs.GetString(key)), 0, Encoding.UTF8.GetBytes(PlayerPrefs.GetString(key)).Length); 29 | result = (T)x.Deserialize(writer); 30 | return true; 31 | } 32 | catch 33 | { 34 | result = default; 35 | return false; 36 | } 37 | 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Multiplayer.Core/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Multiplayer.Core/icon.ico -------------------------------------------------------------------------------- /Multiplayer.Debugging/FileLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.CompilerServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Multiplayer.Shared; 10 | 11 | namespace Multiplayer.Debugging 12 | { 13 | public class FileLogger : ILogger 14 | { 15 | private readonly string logFileName; 16 | private readonly string sourceFilePath; 17 | 18 | private static readonly ConcurrentDictionary _logLocksByFileName = 19 | new ConcurrentDictionary(); 20 | 21 | 22 | /// 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | public FileLogger(string logFileName = "MPLog.txt", [CallerFilePath] string sourceFilePath = "") 29 | { 30 | if (string.IsNullOrEmpty(logFileName)) 31 | { 32 | throw new ArgumentNullException(nameof(logFileName)); 33 | } 34 | 35 | if (!File.Exists(logFileName)) 36 | { 37 | File.Create(logFileName).Close(); 38 | } 39 | 40 | this.logFileName = logFileName; 41 | this.sourceFilePath = sourceFilePath; 42 | _logLocksByFileName.AddOrUpdate(logFileName, new object(), (s, o) => new object()); 43 | } 44 | 45 | public void Log(LogType logType, object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) 46 | { 47 | var sb = new StringBuilder(); 48 | sb.Append($"[{DateTime.Now:HH:mm:ss:ffff}] [MP] [{logType}] [{sourceFilePath}->{memberName}#{sourceLineNumber}] "); 49 | 50 | sb.Append(obj + Environment.NewLine); 51 | if (ex != null) sb.AppendLine(ex.ToString()); 52 | LogInternal(sb.ToString()); 53 | } 54 | 55 | public void Debug(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Debug, obj, ex, memberName, sourceLineNumber); 56 | 57 | public void Info(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Info, obj, ex, memberName, sourceLineNumber); 58 | 59 | public void Warn(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Warn, obj, ex, memberName, sourceLineNumber); 60 | 61 | public void Error(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Error, obj, ex, memberName, sourceLineNumber); 62 | 63 | private void LogInternal(string message) 64 | { 65 | lock (_logLocksByFileName[logFileName]) 66 | { 67 | File.AppendAllText(logFileName, message); 68 | } 69 | } 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Multiplayer.Debugging/Multiplayer.Debugging.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net472 5 | full 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp.dll 15 | 16 | 17 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp-firstpass.dll 18 | false 19 | 20 | 21 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.dll 22 | false 23 | 24 | 25 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.CoreModule.dll 26 | false 27 | 28 | 29 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.Networking.dll 30 | false 31 | 32 | 33 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.StandardEvents.dll 34 | false 35 | 36 | 37 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.TextRenderingModule.dll 38 | false 39 | 40 | 41 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UI.dll 42 | false 43 | 44 | 45 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIElementsModule.dll 46 | false 47 | 48 | 49 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIModule.dll 50 | false 51 | 52 | 53 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UNETModule.dll 54 | false 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Multiplayer.Debugging/UnityLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.CompilerServices; 5 | using UnityEngine; 6 | //using Multiplayer.Extensions; 7 | using Multiplayer.Shared; 8 | using System.Text; 9 | using LogType = Multiplayer.Shared.LogType; 10 | 11 | namespace Multiplayer.Debugging 12 | { 13 | public class UnityLogger : Shared.ILogger 14 | { 15 | private static readonly object externalLogLock = new object(); 16 | private readonly string sourceFilePath; 17 | 18 | public static void Start() 19 | { 20 | // lock (externalLogLock) 21 | // { 22 | // File.Create(Path.Combine(Application.dataPath, "Multiplayer", "latest.log")); 23 | // File.Create(Path.Combine(Application.dataPath, "Multiplayer", DateTime.Now.ToString("HH:mm:ss:ffff") + "-logging.log")); 24 | // } 25 | } 26 | 27 | public static void OnDisable() 28 | { 29 | // lock (externalLogLock) 30 | // { 31 | // File.WriteAllLines(Path.Combine(Application.dataPath, "Multiplayer", "latest.log"), messToArray()); 32 | // File.WriteAllLines(Path.Combine(Application.dataPath, "Multiplayer", DateTime.Now.ToString("HH:mm:ss:ffff").MakeSafe() + "-logging.log"), messageQueue.ToArray()); 33 | // } 34 | } 35 | 36 | public UnityLogger([CallerFilePath] string sourceFilePath = "") 37 | { 38 | this.sourceFilePath = sourceFilePath; 39 | Start(); 40 | } 41 | 42 | public void LogInternal(Shared.LogType logType, string message, Exception ex = null) 43 | { 44 | switch (logType) 45 | { 46 | case Shared.LogType.Debug: 47 | DevConsole.Console.Log(message); 48 | break; 49 | case Shared.LogType.Info: 50 | DevConsole.Console.LogInfo(message); 51 | break; 52 | case Shared.LogType.Error: 53 | DevConsole.Console.LogError(message); 54 | break; 55 | case Shared.LogType.Warn: 56 | DevConsole.Console.LogWarning(message); 57 | break; 58 | } 59 | UnityEngine.Debug.Log(message); 60 | //log queue for file logging 61 | //messageQueue.Enqueue(message); 62 | } 63 | 64 | 65 | #region ILogger Implementation 66 | public void Log(LogType logType, object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) 67 | { 68 | var sb = new StringBuilder(); 69 | sb.Append($"[{DateTime.Now:HH:mm:ss:ffff}] [MP] [{logType}] [{sourceFilePath}->{memberName}#{sourceLineNumber}] "); 70 | 71 | sb.Append(obj + Environment.NewLine); 72 | if (ex != null) sb.AppendLine(ex.ToString()); 73 | LogInternal(logType, sb.ToString()); 74 | } 75 | 76 | public void Debug(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Debug, obj, ex, memberName, sourceLineNumber); 77 | 78 | public void Info(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Info, obj, ex, memberName, sourceLineNumber); 79 | 80 | public void Warn(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Warn, obj, ex, memberName, sourceLineNumber); 81 | 82 | public void Error(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0) => Log(LogType.Error, obj, ex, memberName, sourceLineNumber); 83 | #endregion 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Multiplayer.Extensions/AudioHandler.cs: -------------------------------------------------------------------------------- 1 | namespace Multiplayer.Extensions 2 | { 3 | public static class AudioHandler 4 | { 5 | public static void PlaySound(PopupManager.NotificationSound sfx) 6 | { 7 | UISoundFX.PlaySFX("Notification" + sfx.ToString()); 8 | } 9 | public static void Play(this PopupManager.NotificationSound sfx) 10 | { 11 | UISoundFX.PlaySFX("Notification" + sfx.ToString()); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Multiplayer.Extensions/Multiplayer.Extensions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0;net472 5 | full 6 | 7 | 8 | 9 | 10 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp.dll 11 | false 12 | 13 | 14 | $(softwareincfolder)\Software Inc_Data\Managed\Assembly-CSharp-firstpass.dll 15 | false 16 | 17 | 18 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.dll 19 | false 20 | 21 | 22 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.AudioModule.dll 23 | false 24 | 25 | 26 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.CoreModule.dll 27 | false 28 | 29 | 30 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.Networking.dll 31 | false 32 | 33 | 34 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.StandardEvents.dll 35 | false 36 | 37 | 38 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.TextRenderingModule.dll 39 | false 40 | 41 | 42 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UI.dll 43 | false 44 | 45 | 46 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIElementsModule.dll 47 | false 48 | 49 | 50 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UIModule.dll 51 | false 52 | 53 | 54 | $(softwareincfolder)\Software Inc_Data\Managed\UnityEngine.UNETModule.dll 55 | false 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /Multiplayer.Extensions/Path.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace Multiplayer.Extensions 4 | { 5 | public static class PathUtils 6 | { 7 | public static string AssetsPath = Path.Combine(ModController.ModFolder, "Multiplayer", "Assets"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Multiplayer.Extensions/StringExt.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using UnityEngine; 4 | 5 | namespace Multiplayer.Extensions 6 | { 7 | public static class StringExt 8 | { 9 | private static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); 10 | public static string MakeSafe(this string str) 11 | { 12 | return new string(str.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray()); 13 | } 14 | public static void CopyToClipboard(this string str) 15 | { 16 | //GUIUtility.systemCopyBuffer = str; 17 | } 18 | public static string SetStringVariable(this string str, string varName, string toSet) 19 | { 20 | return str.Replace($"%{varName}%", toSet); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Multiplayer.Extensions/UI.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | 6 | namespace Multiplayer.Extensions 7 | { 8 | public static class UI 9 | { 10 | public static void SetTitle(this GUIWindow Window, string title) 11 | { 12 | Window.InitialTitle = Window.TitleText.text = Window.NonLocTitle = title; 13 | } 14 | public static void SetText(this Button button, string text) 15 | { 16 | button.GetComponentInChildren().text = text; 17 | } 18 | public static void AddElement(this GameObject parent, GameObject element, Rect position, Rect anchors) 19 | { 20 | WindowManager.AddElementToElement(element, parent, position, anchors); 21 | } 22 | public static void AddElement(this GUIWindow parent, GameObject element, Rect position, Rect anchors) 23 | { 24 | WindowManager.AddElementToWindow(element, parent, position, anchors); 25 | } 26 | public static Texture2D LoadPNG(string filePath, int width, int height) 27 | { 28 | Texture2D tex = null; 29 | byte[] fileData; 30 | if (File.Exists(filePath)) 31 | { 32 | fileData = File.ReadAllBytes(filePath); 33 | tex = new Texture2D(width, height); 34 | tex.LoadRawTextureData(fileData); 35 | //tex.LoadImage(fileData); 36 | } 37 | return tex; 38 | } 39 | public static void AddBulk(this List list, params T[] bulkToAdd) 40 | { 41 | foreach (T a in bulkToAdd) 42 | { 43 | list.Add(a); 44 | } 45 | } 46 | public static void AddToElement(this GameObject toAdd, GameObject parent, Rect rect, Rect bounding) 47 | { 48 | WindowManager.AddElementToElement(toAdd, parent, rect, bounding); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Multiplayer.Networking.Test/Multiplayer.Networking.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | false 6 | 7 | 8 | 9 | 10 | 11 | 12 | runtime; build; native; contentfiles; analyzers; buildtransitive 13 | all 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | ..\lib\netstandard2.0\Facepunch.Steamworks.Win64.dll 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Multiplayer.Networking.Test/ServerHandlerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using Multiplayer.Networking.Client; 5 | using Multiplayer.Networking.Server; 6 | using Multiplayer.Networking.Shared; 7 | using Multiplayer.Packets; 8 | using Xunit; 9 | 10 | namespace Multiplayer.Networking.Test 11 | { 12 | public class ServerHandlerTest : IDisposable 13 | { 14 | private static int _serverPort = 1500; 15 | private readonly int serverPort; 16 | private readonly ServerInfo serverInfo; 17 | 18 | private readonly TestLogger logger; 19 | private GameServer server; 20 | 21 | private static readonly ulong userId1 = 0123456789; 22 | private static readonly ulong userId2 = 9876543210; 23 | 24 | private GameClient client1; 25 | private GameClient client2; 26 | 27 | private readonly GameUser testUser1 = new GameUser() { 28 | Id = userId1, 29 | Name = "test-user-1", 30 | Role = UserRole.Host 31 | }; 32 | private readonly GameUser testUser2 = new GameUser() 33 | { 34 | Id = userId2, 35 | Name = "test-user-2", 36 | Role = UserRole.Player 37 | }; 38 | 39 | public ServerHandlerTest() 40 | { 41 | this.serverPort = Interlocked.Increment(ref _serverPort); 42 | this.serverInfo = new ServerInfo() { Port = (ushort)this.serverPort, Name = "testserver", DefaultRole = UserRole.Host }; 43 | this.logger = new TestLogger(); 44 | this.server = new GameServer(logger); 45 | this.client1 = new GameClient(logger, testUser1); 46 | this.client2 = new GameClient(logger, testUser2); 47 | } 48 | 49 | [DebuggerStepThrough] 50 | private void SetupServerAndClient() 51 | { 52 | this.server = new GameServer(logger); 53 | this.server.Start(this.serverInfo); 54 | 55 | this.client1.Connect("localhost", (ushort)serverPort); 56 | this.client2.Connect("localhost", (ushort)serverPort); 57 | } 58 | 59 | public void Dispose() 60 | { 61 | this.server.Dispose(); 62 | } 63 | 64 | [Fact] 65 | public void ServerWithChatHandler() 66 | { 67 | SetupServerAndClient(); 68 | 69 | const string testMessage = "Hello World!"; 70 | this.client1.Send(new ChatMessage() 71 | { 72 | Username = "Client 1", 73 | Contents = testMessage 74 | }); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Multiplayer.Networking.Test/ServerSidedTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using Multiplayer.Networking.Client; 4 | using Multiplayer.Networking.Server; 5 | using Multiplayer.Networking.Shared; 6 | using Xunit; 7 | 8 | namespace Multiplayer.Networking.Test 9 | { 10 | public class ServerSidedTests : IDisposable 11 | { 12 | private static int _serverPort = 1400; 13 | private readonly int serverPort; 14 | private readonly ServerInfo serverInfo = new() { Port = (ushort)_serverPort, Name = "testserver", DefaultRole = UserRole.Host }; 15 | 16 | private readonly TestLogger logger; 17 | private readonly GameServer server; 18 | private readonly GameUser testUser = new GameUser() 19 | { 20 | Id = 0123456789, 21 | Name = "test-user", 22 | Role = UserRole.Host 23 | }; 24 | 25 | public ServerSidedTests() 26 | { 27 | this.serverPort = Interlocked.Increment(ref _serverPort); 28 | this.logger = new TestLogger(); 29 | this.server = new GameServer(this.logger); 30 | } 31 | 32 | private GameClient CreateClient() 33 | { 34 | var client = new GameClient(this.logger, this.testUser); 35 | client.Connect("localhost", (ushort)serverPort); 36 | return client; 37 | } 38 | 39 | public void Dispose() 40 | { 41 | this.server.Dispose(); 42 | } 43 | 44 | [Fact] 45 | public void Bootup() 46 | { 47 | Assert.NotNull(server.SocketManager); 48 | Assert.Null(server.ServerInfo); 49 | } 50 | 51 | [Fact] 52 | public void StartStop() 53 | { 54 | var startedEventFired = false; 55 | var stoppedEventFired = false; 56 | 57 | server.ServerStarted += (sender, e) => { startedEventFired = true; }; 58 | server.ServerStopped += (sender, e) => { stoppedEventFired = true; }; 59 | 60 | server.Start(serverInfo); 61 | 62 | Assert.NotNull(server.SocketManager); 63 | //Assert.True(server.RawServer.Active); 64 | 65 | Assert.NotNull(server.ServerInfo); 66 | 67 | Assert.Equal(serverPort, server.ServerInfo.Port); 68 | Assert.Equal("", server.ServerInfo.Password); 69 | 70 | server.Stop(); 71 | 72 | //Assert.False(server.RawServer.Active); 73 | 74 | Assert.True(startedEventFired); 75 | Assert.True(stoppedEventFired); 76 | } 77 | 78 | [Fact] 79 | public void ClientConnected() 80 | { 81 | var clientConnectedFired = false; 82 | var clientDisconnectedFired = false; 83 | 84 | var connectionId = -1; 85 | 86 | server.ClientConnected += (sender, e) => { 87 | clientConnectedFired = true; 88 | //Assert.NotEqual(-1, e.ConnectionId); 89 | //connectionId = e.ConnectionId; 90 | //Assert.False(e.Cancel); 91 | }; 92 | server.ClientDisconnected += (sender, e) => { 93 | clientDisconnectedFired = true; 94 | // Assert.NotEqual(-1, connectionId); 95 | //Assert.Equal(connectionId, e.ConnectionId); 96 | }; 97 | 98 | server.Start(this.serverInfo); 99 | 100 | using var client = this.CreateClient(); 101 | client.Connect("127.0.0.1", serverInfo.Port); 102 | 103 | Assert.True(clientConnectedFired); 104 | Assert.NotEqual(-1, connectionId); 105 | //Assert.Contains(connectionId, server.ConnectedClients); 106 | Assert.Equal(1, server.UserManager.Count()); 107 | 108 | //Assert.True(client.RawClient.Connected); 109 | 110 | client.Disconnect(); 111 | 112 | Assert.Equal(0, server.UserManager.Count()); 113 | 114 | // Assert.True(clientDisconnectedFired); 115 | } 116 | 117 | [Fact] 118 | public void DenyClient() 119 | { 120 | var clientConnectedFired = false; 121 | var clientDisconnectedFired = false; 122 | 123 | var connectionId = -1; 124 | 125 | server.ClientConnected += (sender, e) => { 126 | clientConnectedFired = true; 127 | //Assert.NotEqual(-1, e.ConnectionId); 128 | //connectionId = e.ConnectionId; 129 | //e.Cancel = true; 130 | }; 131 | 132 | server.ClientDisconnected += (sender, e) => { 133 | clientDisconnectedFired = true; 134 | //Assert.Equal(connectionId, e.ConnectionId); 135 | }; 136 | 137 | server.Start(this.serverInfo); 138 | 139 | using var client = this.CreateClient(); 140 | 141 | Assert.Equal(0, server.UserManager.Count()); 142 | } 143 | 144 | // [Fact] 145 | // public void ClientHandshake() 146 | // { 147 | // var clientConnectedFired = false; 148 | // var handshakeReceived = false; 149 | // 150 | // var connectionId = -1; 151 | // 152 | // server.ClientConnected += (sender, e) => { 153 | // clientConnectedFired = true; 154 | // //Assert.NotEqual(-1, e.ConnectionId); 155 | // //connectionId = e.ConnectionId; 156 | // //Assert.False(e.Cancel); 157 | // }; 158 | // /*server.ReceivedPacket += (sender, e) => 159 | // { 160 | // if (e.Handled) 161 | // return; 162 | // handshakeReceived = e.Handled = e.Packet is Handshake; 163 | // };*/ 164 | // //server.RegisterPacketHandler(); 165 | // 166 | // server.Start(this.serverInfo); 167 | // 168 | // using var client = this.CreateClient(); 169 | // 170 | // Assert.True(handshakeReceived); 171 | // 172 | // //Assert.True(client.RawClient.Connected); 173 | // 174 | // client.Disconnect(); 175 | // 176 | // Assert.True(clientConnectedFired); 177 | // } 178 | // 179 | // [Fact(Skip = "It's not ready yet")] 180 | // public void ClientDoubleHandshake() 181 | // { 182 | // /*server.ReceivedPacket += (sender, e) => 183 | // { 184 | // if (e.Handled) 185 | // return; 186 | // e.Handled |= e.Packet is Handshake; 187 | // };*/ 188 | // server.Start(this.serverInfo); 189 | // 190 | // using var client = this.CreateClient(); 191 | // 192 | // server.SafeHandleMessages(); // finish server connected 193 | // client.SafeHandleMessages(); // trigger handshake 194 | // server.SafeHandleMessages(); // handle client handshake 195 | // 196 | // var handshake = new Handshake(0123456789, "placeholder"); 197 | // //client.RawClient.Send(this.packetSerializer.SerializePacket(handshake)); 198 | // server.SafeHandleMessages(); 199 | // client.SafeHandleMessages(); 200 | // 201 | // // TODO dont know what to assert here as this is undefined behaviour so far 202 | // } 203 | // 204 | // [Fact] 205 | // public void ClientHandshakeDisconnect() 206 | // { 207 | // var disconnectReceived = false; 208 | // /*server.ReceivedPacket += (sender, e) => 209 | // { 210 | // if (e.Handled) 211 | // return; 212 | // e.Handled |= e.Packet is Handshake; 213 | // }; 214 | // server.ReceivedPacket += (sender, e) => 215 | // { 216 | // if (e.Handled) 217 | // return; 218 | // if(e.Packet is Disconnect dc) 219 | // { 220 | // disconnectReceived = true; 221 | // Assert.Equal(DisconnectReason.Leaving, dc.Reason); 222 | // } 223 | // e.Handled |= disconnectReceived; 224 | // };*/ 225 | // 226 | // server.Start(this.serverInfo); 227 | // 228 | // using var client = this.CreateClient(); 229 | // 230 | // server.SafeHandleMessages(); // finish server connected 231 | // client.SafeHandleMessages(); // trigger handshake 232 | // server.SafeHandleMessages(); // handle client handshake 233 | // 234 | // client.Disconnect(); 235 | // 236 | // server.SafeHandleMessages(); 237 | // client.SafeHandleMessages(); 238 | // //server.SafeHandleMessages(); 239 | // 240 | // Assert.True(disconnectReceived); 241 | // } 242 | // 243 | // [Fact] 244 | // public void ServerWithChatHandler() 245 | // { 246 | // var chatHandler = new ChatHandler(this.server); 247 | // this.server.RegisterPacketHandler(chatHandler); 248 | // 249 | // this.server.Start(this.serverInfo); 250 | // 251 | // using var client = this.CreateClient(); 252 | // 253 | // server.SafeHandleMessages(); // finish server connected 254 | // client.SafeHandleMessages(); // trigger handshake 255 | // server.SafeHandleMessages(); // handle client handshake 256 | // 257 | // client.Disconnect(); 258 | // 259 | // server.SafeHandleMessages(); 260 | // client.SafeHandleMessages(); 261 | // 262 | // //Assert.Equal(new Dictionary>() { chatHandler } ,this.server.PacketHandlers); 263 | // } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /Multiplayer.Networking.Test/SteamWorksTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Threading; 4 | using Facepunch.Steamworks; 5 | using Facepunch.Steamworks.Data; 6 | using Multiplayer.Networking.Client; 7 | using Multiplayer.Networking.Server; 8 | using Multiplayer.Networking.Shared.Managers; 9 | using Xunit; 10 | using Xunit.Abstractions; 11 | 12 | namespace Multiplayer.Networking.Test 13 | { 14 | public class SteamWorksTests : IDisposable 15 | { 16 | private const int Appid = 362620; 17 | private const int NetworkTimeout = 5000; 18 | private static int _port = 1024; 19 | private readonly ushort Port; 20 | private readonly ITestOutputHelper outputHelper; 21 | 22 | public SteamWorksTests(ITestOutputHelper outputHelper) 23 | { 24 | this.Port = (ushort)Interlocked.Increment(ref _port); 25 | SteamClient.Init(Appid); 26 | this.outputHelper = outputHelper; 27 | } 28 | 29 | public void Dispose() 30 | { 31 | SteamClient.Shutdown(); 32 | } 33 | 34 | [Fact] 35 | public void InitServer() 36 | { 37 | var gameServerSocket = SteamNetworkingSockets.CreateNormalSocket(NetAddress.AnyIp(this.Port)); 38 | Assert.NotNull(gameServerSocket); 39 | Assert.True(gameServerSocket.Close()); 40 | } 41 | 42 | [Fact] 43 | public void InitClient() 44 | { 45 | var gameClientSocket = SteamNetworkingSockets.ConnectNormal(NetAddress.LocalHost(this.Port)); 46 | Assert.NotNull(gameClientSocket); 47 | gameClientSocket.Close(); 48 | } 49 | 50 | [Fact] 51 | public void InitialConnect() 52 | { 53 | var gameServerSocket = SteamNetworkingSockets.CreateNormalSocket(NetAddress.AnyIp(this.Port)); 54 | 55 | var gameClientSocket = SteamNetworkingSockets.ConnectNormal(NetAddress.LocalHost(this.Port)); 56 | 57 | Assert.True(gameClientSocket.ConnectedEvent.WaitOne(NetworkTimeout)); 58 | Assert.True(gameServerSocket.ConnectedEvent.WaitOne(NetworkTimeout)); 59 | 60 | //gameClientSocket.Connection.SendMessage() 61 | 62 | gameClientSocket.Close(); 63 | 64 | Assert.True(gameServerSocket.DisconnectedEvent.WaitOne(NetworkTimeout)); 65 | Assert.True(gameClientSocket.DisconnectedEvent.WaitOne(NetworkTimeout)); 66 | } 67 | 68 | [Fact] 69 | public void TestPacketManager() 70 | { 71 | var log = new TestLogger(); 72 | var gs = new GameServer(log); 73 | var gc = new GameClient(log, null); 74 | RegisterManager.LoadInstances(log, gc, gs); 75 | log.Info(RegisterManager.ClientPacketHandlersCache); 76 | log.Info(RegisterManager.ServerPacketHandlersCache); 77 | } 78 | 79 | 80 | [Fact] 81 | public void Test() 82 | { 83 | var gameServerSocket = SteamNetworkingSockets.CreateNormalSocket(NetAddress.AnyIp(this.Port)); 84 | 85 | var gameClientSocket = SteamNetworkingSockets.ConnectNormal(NetAddress.LocalHost(this.Port)); 86 | 87 | Assert.True(gameClientSocket.ConnectedEvent.WaitOne(NetworkTimeout)); 88 | Assert.True(gameServerSocket.ConnectedEvent.WaitOne(NetworkTimeout)); 89 | 90 | //gameClientSocket.Connection.SendMessage() 91 | 92 | gameClientSocket.Close(); 93 | 94 | Assert.True(gameServerSocket.DisconnectedEvent.WaitOne(NetworkTimeout)); 95 | Assert.True(gameClientSocket.DisconnectedEvent.WaitOne(NetworkTimeout)); 96 | } 97 | 98 | [Fact] 99 | public void AppDomainChecks() 100 | { 101 | var domain = AppDomain.CurrentDomain; 102 | var assembly = Assembly.GetExecutingAssembly(); 103 | 104 | domain.AssemblyResolve += Domain_AssemblyResolve; 105 | domain.AssemblyLoad += Domain_AssemblyLoad; 106 | 107 | var references = assembly.GetReferencedAssemblies(); 108 | //assembly. 109 | } 110 | 111 | private void Domain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) 112 | { 113 | outputHelper.WriteLine($"{args.LoadedAssembly.FullName} AssemblyLoad"); 114 | } 115 | 116 | private Assembly Domain_AssemblyResolve(object sender, ResolveEventArgs args) 117 | { 118 | outputHelper.WriteLine($"{args.RequestingAssembly.FullName} AssemlbyResolve {args.Name}"); 119 | return null; 120 | } 121 | } 122 | 123 | internal class TestServer : SocketManager 124 | { 125 | public ManualResetEvent ConnectedEvent = new ManualResetEvent(false); 126 | public ManualResetEvent DisconnectedEvent = new ManualResetEvent(false); 127 | public ManualResetEvent MessageEvent = new ManualResetEvent(false); 128 | 129 | public override void OnConnected(Connection connection, ConnectionInfo info) 130 | { 131 | ConnectedEvent.Set(); 132 | } 133 | 134 | public override void OnConnecting(Connection connection, ConnectionInfo info) 135 | { 136 | connection.Accept(); 137 | } 138 | 139 | public override void OnDisconnected(Connection connection, ConnectionInfo info) 140 | { 141 | DisconnectedEvent.Set(); 142 | } 143 | 144 | public override void OnMessage(Connection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel) 145 | { 146 | MessageEvent.Set(); 147 | } 148 | } 149 | 150 | internal class TestClient : ConnectionManager 151 | { 152 | public ManualResetEvent ConnectedEvent = new ManualResetEvent(false); 153 | public ManualResetEvent DisconnectedEvent = new ManualResetEvent(false); 154 | public ManualResetEvent MessageEvent = new ManualResetEvent(false); 155 | public override void OnConnected(ConnectionInfo info) 156 | { 157 | ConnectedEvent.Set(); 158 | } 159 | 160 | public override void OnConnecting(ConnectionInfo info) 161 | { 162 | } 163 | 164 | public override void OnDisconnected(ConnectionInfo info) 165 | { 166 | DisconnectedEvent.Set(); 167 | } 168 | 169 | public override void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel) 170 | { 171 | MessageEvent.Set(); 172 | } 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /Multiplayer.Networking.Test/TestLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Multiplayer.Shared; 3 | 4 | namespace Multiplayer.Networking.Test 5 | { 6 | internal class TestLogger : ILogger 7 | { 8 | #region ILogger Implementation 9 | public void Log(LogType logType, object obj, Exception ex = null, string memberName = "", int sourceLineNumber = 0) 10 | { 11 | Console.WriteLine("[" + logType.ToString().ToUpper() + "] " + memberName + "@" + sourceLineNumber + " - " + obj); 12 | } 13 | 14 | public void Debug(object obj, Exception ex = null, string memberName = "", int sourceLineNumber = 0) 15 | { 16 | Log(LogType.Debug, obj, ex, memberName, sourceLineNumber); 17 | } 18 | 19 | public void Info(object obj, Exception ex = null, string memberName = "", int sourceLineNumber = 0) 20 | { 21 | Log(LogType.Info, obj, ex, memberName, sourceLineNumber); 22 | } 23 | 24 | public void Warn(object obj, Exception ex = null, string memberName = "", int sourceLineNumber = 0) 25 | { 26 | Log(LogType.Warn, obj, ex, memberName, sourceLineNumber); 27 | } 28 | 29 | public void Error(object obj, Exception ex = null, string memberName = "", int sourceLineNumber = 0) 30 | { 31 | Log(LogType.Error, obj, ex, memberName, sourceLineNumber); 32 | } 33 | #endregion 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/ClientEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Multiplayer.Networking.Shared; 3 | 4 | namespace Multiplayer.Networking.Client 5 | { 6 | public class ClientConnectedEventArgs : EventArgs 7 | { 8 | public ClientConnectedEventArgs(int connectionId) 9 | { 10 | this.ConnectionId = connectionId; 11 | } 12 | public int ConnectionId { get; set; } 13 | } 14 | 15 | public class ClientDisconnectedEventArgs : EventArgs 16 | { 17 | public ClientDisconnectedEventArgs(int connectionId) 18 | { 19 | this.ConnectionId = connectionId; 20 | } 21 | public int ConnectionId { get; set; } 22 | } 23 | 24 | public class UserConnectedEventArgs : EventArgs 25 | { 26 | public UserConnectedEventArgs(GameUser user) 27 | { 28 | this.User = user; 29 | } 30 | public GameUser User { get; set; } 31 | } 32 | 33 | public class UserDisconnectedEventArgs : EventArgs 34 | { 35 | public UserDisconnectedEventArgs(GameUser user) 36 | { 37 | this.User = user; 38 | } 39 | public GameUser User { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/GameClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Facepunch.Steamworks; 4 | using Facepunch.Steamworks.Data; 5 | using Google.Protobuf; 6 | using Multiplayer.Networking.Shared; 7 | using Multiplayer.Networking.Shared.Managers; 8 | using Multiplayer.Packets; 9 | using Multiplayer.Shared; 10 | 11 | namespace Multiplayer.Networking.Client 12 | { 13 | public class GameClient : IDisposable 14 | { 15 | 16 | public event EventHandler ConnectionStateChange; 17 | 18 | private readonly GameUser? virtualUser; 19 | public GameClientSocket Socket { get; private set; } 20 | private ILogger log { get; set; } 21 | 22 | public GameClient(ILogger log, GameUser? virtualUser) 23 | { 24 | this.log = log; 25 | this.virtualUser = virtualUser; 26 | RegisterManager.LoadInstances(log, this, null); 27 | } 28 | 29 | public void Connect(string ip, ushort port, string password = "") 30 | { 31 | this.Socket = SteamNetworkingSockets.ConnectNormal(NetAddress.From(ip, port)); 32 | 33 | this.Socket.Parent = this; 34 | 35 | if (virtualUser != null) 36 | { 37 | Send(new Handshake 38 | { 39 | Username = virtualUser.Name, 40 | Password = password, 41 | Id = virtualUser.Id 42 | }); 43 | return; 44 | } 45 | 46 | var handshake = new Handshake 47 | { 48 | Username = SteamClient.Name, 49 | Password = password, 50 | Id = SteamClient.SteamId.Value 51 | }; 52 | this.Send(handshake); 53 | this?.ConnectionStateChange(this, true); 54 | } 55 | 56 | public void Disconnect() 57 | { 58 | this?.ConnectionStateChange(this, false); 59 | Socket.Connection.Close(); 60 | } 61 | 62 | public unsafe void Send(T message) where T : IMessage 63 | { 64 | //maybe we should avoid this lock 65 | log.Debug($"Sending message of type {typeof(T)}: {message.ToString()}"); 66 | using (var serializationStream = new MemoryStream()) 67 | { 68 | message.WriteTo(serializationStream); 69 | var buffer = serializationStream.GetBuffer(); 70 | var messageSize = message.CalculateSize(); 71 | fixed (byte* p = buffer) 72 | { 73 | this.Socket.Connection.SendMessage((IntPtr)p, messageSize); 74 | 75 | } 76 | serializationStream.Position = 0; 77 | } 78 | 79 | log.Debug($"Message sent"); 80 | 81 | } 82 | 83 | public void Dispose() 84 | { 85 | Socket?.Connection.Close(); 86 | Socket?.Dispose(); 87 | Socket = null; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/GameClientSocket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Runtime.InteropServices; 4 | using Facepunch.Steamworks; 5 | using Facepunch.Steamworks.Data; 6 | using Multiplayer.Debugging; 7 | using Multiplayer.Networking.Shared; 8 | using Multiplayer.Networking.Shared.Managers; 9 | using Multiplayer.Packets; 10 | using Multiplayer.Shared; 11 | 12 | namespace Multiplayer.Networking.Client 13 | { 14 | public class GameClientSocket : ConnectionManager, IDisposable 15 | { 16 | private readonly ILogger log; 17 | 18 | public GameClient Parent; 19 | 20 | private readonly ArrayPool bufferPool = ArrayPool.Create(); 21 | 22 | public GameClientSocket() 23 | { 24 | log = new UnityLogger(); 25 | } 26 | 27 | public override void OnConnected(ConnectionInfo info) 28 | { 29 | base.OnConnected(info); 30 | } 31 | 32 | public override void OnConnecting(ConnectionInfo info) 33 | { 34 | base.OnConnecting(info); 35 | } 36 | 37 | 38 | //base implementation is good enaugh 39 | /*public override void OnConnectionChanged(ConnectionInfo info) 40 | { 41 | base.OnConnectionChanged(info); 42 | }*/ 43 | 44 | public override void OnDisconnected(ConnectionInfo info) 45 | { 46 | this.Close(); 47 | base.OnDisconnected(info); 48 | } 49 | 50 | public override void OnMessage(IntPtr data, int size, long messageNum, long recvTime, int channel) 51 | { 52 | log.Debug($"On Message, size: {size}, messageNum: {messageNum}"); 53 | 54 | // TODO this should have a reasonable size 55 | if (size > 10 * 1024 * 1024) // 10kb 56 | { 57 | log.Warn("Discarding large packet"); 58 | return; 59 | } 60 | 61 | var buffer = this.bufferPool.Rent(size); 62 | Marshal.Copy(data, buffer, 0, size); 63 | 64 | var gamePacket = GamePacket.Parser.ParseFrom(buffer, 0, size); 65 | 66 | if (gamePacket.PacketCase == GamePacket.PacketOneofCase.None) return; 67 | if (RegisterManager.ClientPacketHandlersCache.TryGetValue(gamePacket.PacketCase, out var handlers)) 68 | { 69 | foreach (IPacketHandler handler in handlers) 70 | { 71 | handler.HandlePacket(null, gamePacket); 72 | } 73 | } 74 | } 75 | 76 | public void Dispose() 77 | { 78 | try 79 | { 80 | if (this.Connected || Connecting) 81 | { 82 | this.Connection.Close(true); 83 | Close(); 84 | } 85 | } 86 | catch (Exception ex) 87 | { 88 | this.log.Error(ex); 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/GameClient_old.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // 3 | // using Multiplayer.Networking.Utility; 4 | // using Multiplayer.Shared; 5 | // 6 | // using Packets; 7 | // using System.Threading; 8 | // using Multiplayer.Networking.Shared; 9 | // using System.Collections.Generic; 10 | // using System.Linq; 11 | // using Facepunch.Steamworks; 12 | // using Facepunch.Steamworks.Data; 13 | // 14 | // namespace Multiplayer.Networking.Client 15 | // { 16 | // public class GameClient_old : ISocketManager, IDisposable 17 | // { 18 | // #region Events 19 | // public event EventHandler ClientConnected; 20 | // public event EventHandler ClientDisconnected; 21 | // public event EventHandler ConnectionReady; 22 | // public event EventHandler UserConnected; 23 | // public event EventHandler UserDisconnected; 24 | // #endregion 25 | // 26 | // private readonly ILogger logger; 27 | // private readonly PacketSerializer packetSerializer; 28 | // public IUserManager UserManager { get; } 29 | // private readonly GameUser gameUser; 30 | // 31 | // public GameClient_old(ILogger logger, GameUser user, PacketSerializer packetSerializer, IUserManager userManager) 32 | // { 33 | // this.logger = logger; 34 | // this.packetSerializer = packetSerializer; 35 | // this.UserManager = userManager; 36 | // this.gameUser = user; 37 | // try 38 | // { 39 | // SteamClient.Init(362620); 40 | // } 41 | // catch (Exception ex) 42 | // { 43 | // this.logger.Error(ex); 44 | // } 45 | // } 46 | // 47 | // public void Dispose() 48 | // { 49 | // // disconnect the client 50 | // this.Disconnect(); 51 | // // clear all events 52 | // this.ClientConnected = null; 53 | // this.ClientDisconnected = null; 54 | // 55 | // SteamClient.Shutdown(); 56 | // } 57 | // 58 | // public SocketManager RawClient { get; private set; } 59 | // 60 | // public void Send(IPacket packet) 61 | // { 62 | // // maybe add a check if we are still connected 63 | // /*if (!this.RawClient.Send(this.packetSerializer.SerializePacket(packet))) 64 | // { 65 | // this.logger.Error("could not send packet"); 66 | // }*/ 67 | // } 68 | // 69 | // private void HandleWelcomeUser(WelcomeUser welcomeUser) 70 | // { 71 | // this.logger.Debug($"[client] welcome client {welcomeUser.Sender}, {welcomeUser.UserName}"); 72 | // 73 | // var newUser = this.UserManager.GetOrAddUser(new GameUser() 74 | // { 75 | // Id = welcomeUser.Sender, 76 | // Name = welcomeUser.UserName, 77 | // }); 78 | // 79 | // if (welcomeUser.Sender == this.gameUser.Id) 80 | // this.ConnectionReady?.Invoke(this, null); 81 | // this.UserConnected?.Invoke(this, new UserConnectedEventArgs(newUser)); 82 | // } 83 | // 84 | // private void HandleDisconnect(GameUser user, Disconnect disconnect) 85 | // { 86 | // var disconnectedUserId = disconnect.Sender; 87 | // 88 | // if (disconnectedUserId == 0UL || disconnectedUserId == this.gameUser.Id) 89 | // { 90 | // // duh something bad happened and this client is doomed... 91 | // //this.RawClient.Disconnect(); 92 | // return; 93 | // } 94 | // 95 | // this.UserManager.RemoveUser(user); 96 | // this.logger.Debug($"[client] removing client {disconnectedUserId}"); 97 | // } 98 | // 99 | // /*private void InternalHandleMessage(Message msg) 100 | // { 101 | // switch (msg.eventType) 102 | // { 103 | // case EventType.Connected: 104 | // this.ClientConnected?.Invoke(this, new ClientConnectedEventArgs(msg.connectionId)); 105 | // 106 | // this.Send(new Handshake(this.gameUser.Id, this.gameUser.Name)); 107 | // this.logger.Debug("[client] connected to server"); 108 | // break; 109 | // case EventType.Data: 110 | // 111 | // // TODO check msg.connectionId. It should not change 112 | // IPacket packet = null; 113 | // try 114 | // { 115 | // packet = this.packetSerializer.DeserializePacket(msg.data); 116 | // } catch(Exception) 117 | // { 118 | // // ignore exceptions 119 | // } 120 | // if (packet == null) 121 | // { 122 | // // maybe add some more details 123 | // this.logger.Warn("[client] received unknown packet"); 124 | // break; 125 | // } 126 | // 127 | // if (packet is WelcomeUser welcomeUser) 128 | // { 129 | // this.HandleWelcomeUser(welcomeUser); 130 | // break; 131 | // } 132 | // 133 | // var gameUser = this.UserManager.GetUser(packet.Sender); 134 | // 135 | // if (gameUser == null && packet.Sender != 0UL) 136 | // { 137 | // // this packet is not sent by the server and has no known user 138 | // this.logger.Warn($"[client] incompleted handshake {packet.Sender}"); 139 | // break; 140 | // } 141 | // 142 | // if (packet is Disconnect disconnect) 143 | // { 144 | // this.HandleDisconnect(gameUser, disconnect); 145 | // break; 146 | // } 147 | // 148 | // if (this.bakedHandlers.TryGetValue(packet.GetType(), out var packetHandlers)) 149 | // { 150 | // foreach (var packetHandler in packetHandlers) 151 | // { 152 | // packetHandler.HandlePacket(gameUser, packet); 153 | // } 154 | // } 155 | // else 156 | // { 157 | // // there is no packet handler here :( 158 | // this.logger.Warn($"[client] missing packet handler {packet.GetType()}"); 159 | // } 160 | // 161 | // break; 162 | // case EventType.Disconnected: 163 | // 164 | // // maybe we should fire a userdisconnected for all users here? 165 | // this.UserManager.Clear(); 166 | // this.ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(msg.connectionId)); 167 | // this.logger.Debug("[client] disconnected from server"); 168 | // break; 169 | // } 170 | // }*/ 171 | // 172 | // public bool HandleMessages() 173 | // { 174 | // var hadMessage = false; 175 | // 176 | // /*while (this.RawClient.GetNextMessage(out Message msg)) 177 | // { 178 | // hadMessage = true; 179 | // this.InternalHandleMessage(msg); 180 | // }*/ 181 | // return hadMessage; 182 | // } 183 | // 184 | // public void Connect(string ip, ushort port) 185 | // { 186 | // this.RawClient = SteamNetworkingSockets.CreateNormalSocket(NetAddress.From(ip, port), this); 187 | // } 188 | // 189 | // public void Disconnect() 190 | // { 191 | // // TODO this send does not really work as the disconnect kills the connection 192 | // this.Send(new Disconnect(this.gameUser.Id, DisconnectReason.Leaving)); 193 | // //this.RawClient.Disconnect(); 194 | // } 195 | // 196 | // private readonly Dictionary> packetHandlers = new Dictionary>(); 197 | // private readonly Dictionary> bakedHandlers = new Dictionary>(); 198 | // //public IReadOnlyDictionary> PacketHandlers { get => this.bakedHandlers; } 199 | // 200 | // public void RegisterPacketHandler(IPacketHandler packetHandler) 201 | // { 202 | // foreach (var packetType in packetHandler.PacketsFilter) 203 | // { 204 | // if (!this.packetHandlers.TryGetValue(packetType, out List<(int, IPacketHandler)> handlers)) 205 | // { 206 | // handlers = new List<(int, IPacketHandler)>(); 207 | // this.packetHandlers.Add(packetType, handlers); 208 | // } 209 | // handlers.Add((packetHandler.Priority, packetHandler)); 210 | // } 211 | // } 212 | // 213 | // private void BakeHandlers() 214 | // { 215 | // this.bakedHandlers.Clear(); 216 | // foreach (var handlerKeyValue in this.packetHandlers) 217 | // { 218 | // this.bakedHandlers.Add(handlerKeyValue.Key, 219 | // handlerKeyValue.Value 220 | // .OrderBy(x => x.priority) 221 | // .Select(x => x.handler) 222 | // .ToList()); 223 | // } 224 | // } 225 | // 226 | // public void OnConnecting(Connection connection, ConnectionInfo info) 227 | // { 228 | // connection.Accept(); 229 | // } 230 | // 231 | // public void OnConnected(Connection connection, ConnectionInfo info) 232 | // { 233 | // this.logger.Debug($"[client] connected to {info.Address.Address}"); 234 | // } 235 | // 236 | // public void OnDisconnected(Connection connection, ConnectionInfo info) 237 | // { 238 | // this.logger.Debug($"[client] disconnected"); 239 | // } 240 | // 241 | // public void OnMessage(Connection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel) 242 | // { 243 | // this.logger.Debug($"[client] message"); 244 | // } 245 | // } 246 | // } 247 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/Handlers/ChatHandler.cs: -------------------------------------------------------------------------------- 1 | using Multiplayer.Networking.Shared; 2 | using Multiplayer.Networking.Shared.Managers; 3 | using Multiplayer.Packets; 4 | 5 | namespace Multiplayer.Networking.Client.Handlers 6 | { 7 | [RegisterManager(RegisterType.Client, GamePacket.PacketOneofCase.ChatMessage)] 8 | public class ChatHandler : ClientPacketHandler 9 | { 10 | public ChatHandler(GameClient client) : base(client) { } 11 | public override void HandlePacket(GameUser sender, ChatMessage packet) 12 | { 13 | // TODO: Fix this 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Client/Handlers/ClientPacketHandler.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using Multiplayer.Networking.Shared; 3 | using Multiplayer.Packets; 4 | 5 | namespace Multiplayer.Networking.Client 6 | { 7 | public abstract class ClientPacketHandler : IPacketHandler where T : IMessage 8 | { 9 | public virtual int Priority => 0; 10 | public void HandlePacket(IPacketHandler.PacketSender sender, GamePacket packet) 11 | { 12 | var fields = packet.GetType().GetFields(); 13 | foreach (var field in fields) 14 | { 15 | if (field.FieldType == typeof(T)) 16 | { 17 | HandlePacket(sender.User, (T)field.GetValue(packet)!); 18 | } 19 | } 20 | } 21 | 22 | public abstract void HandlePacket(GameUser sender, T packet); 23 | 24 | protected readonly GameClient client; 25 | 26 | public ClientPacketHandler(GameClient client) 27 | { 28 | this.client = client; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Constants.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Multiplayer.Networking 3 | { 4 | public enum DisconnectReason 5 | { 6 | ServerStop, 7 | Leaving, 8 | InvalidPassword, 9 | Kicked, 10 | Banned, 11 | UnhandledPacket, 12 | InvalidHandshake 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Multiplayer.Networking.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net472 5 | true 6 | 8.0 7 | enable 8 | full 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | ..\lib\netstandard2.0\Facepunch.Steamworks.Win64.dll 27 | 28 | 29 | 30 | ..\lib\net46\Facepunch.Steamworks.Win64.dll 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/AuthResponse.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: authResponse.proto 4 | // 5 | #pragma warning disable 1591, 0612, 3021 6 | #region Designer generated code 7 | 8 | using pb = global::Google.Protobuf; 9 | using pbc = global::Google.Protobuf.Collections; 10 | using pbr = global::Google.Protobuf.Reflection; 11 | using scg = global::System.Collections.Generic; 12 | namespace Multiplayer.Packets { 13 | 14 | /// Holder for reflection information generated from authResponse.proto 15 | public static partial class AuthResponseReflection { 16 | 17 | #region Descriptor 18 | /// File descriptor for authResponse.proto 19 | public static pbr::FileDescriptor Descriptor { 20 | get { return descriptor; } 21 | } 22 | private static pbr::FileDescriptor descriptor; 23 | 24 | static AuthResponseReflection() { 25 | byte[] descriptorData = global::System.Convert.FromBase64String( 26 | string.Concat( 27 | "ChJhdXRoUmVzcG9uc2UucHJvdG8SE011bHRpcGxheWVyLlBhY2tldHMaEG1v", 28 | "ZGVyYXRpb24ucHJvdG8ihgEKDEF1dGhSZXNwb25zZRIvCgRUeXBlGAEgASgO", 29 | "MiEuTXVsdGlwbGF5ZXIuUGFja2V0cy5SZXNwb25zZVR5cGUSOQoHQmFuSW5m", 30 | "bxgCIAEoCzIjLk11bHRpcGxheWVyLlBhY2tldHMuQmFuSW5mb3JtYXRpb25I", 31 | "AIgBAUIKCghfQmFuSW5mbyotCgxSZXNwb25zZVR5cGUSCAoER09PRBAAEgcK", 32 | "A0JBRBABEgoKBkJBTk5FRBACYgZwcm90bzM=")); 33 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 34 | new pbr::FileDescriptor[] { global::Multiplayer.Packets.ModerationReflection.Descriptor, }, 35 | new pbr::GeneratedClrTypeInfo(new[] {typeof(global::Multiplayer.Packets.ResponseType), }, null, new pbr::GeneratedClrTypeInfo[] { 36 | new pbr::GeneratedClrTypeInfo(typeof(global::Multiplayer.Packets.AuthResponse), global::Multiplayer.Packets.AuthResponse.Parser, new[]{ "Type", "BanInfo" }, new[]{ "BanInfo" }, null, null, null) 37 | })); 38 | } 39 | #endregion 40 | 41 | } 42 | #region Enums 43 | public enum ResponseType { 44 | [pbr::OriginalName("GOOD")] Good = 0, 45 | [pbr::OriginalName("BAD")] Bad = 1, 46 | [pbr::OriginalName("BANNED")] Banned = 2, 47 | } 48 | 49 | #endregion 50 | 51 | #region Messages 52 | public sealed partial class AuthResponse : pb::IMessage 53 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 54 | , pb::IBufferMessage 55 | #endif 56 | { 57 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new AuthResponse()); 58 | private pb::UnknownFieldSet _unknownFields; 59 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 60 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 61 | public static pb::MessageParser Parser { get { return _parser; } } 62 | 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 64 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 65 | public static pbr::MessageDescriptor Descriptor { 66 | get { return global::Multiplayer.Packets.AuthResponseReflection.Descriptor.MessageTypes[0]; } 67 | } 68 | 69 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 70 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 71 | pbr::MessageDescriptor pb::IMessage.Descriptor { 72 | get { return Descriptor; } 73 | } 74 | 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 76 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 77 | public AuthResponse() { 78 | OnConstruction(); 79 | } 80 | 81 | partial void OnConstruction(); 82 | 83 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 84 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 85 | public AuthResponse(AuthResponse other) : this() { 86 | type_ = other.type_; 87 | banInfo_ = other.banInfo_ != null ? other.banInfo_.Clone() : null; 88 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 89 | } 90 | 91 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 92 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 93 | public AuthResponse Clone() { 94 | return new AuthResponse(this); 95 | } 96 | 97 | /// Field number for the "Type" field. 98 | public const int TypeFieldNumber = 1; 99 | private global::Multiplayer.Packets.ResponseType type_ = global::Multiplayer.Packets.ResponseType.Good; 100 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 101 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 102 | public global::Multiplayer.Packets.ResponseType Type { 103 | get { return type_; } 104 | set { 105 | type_ = value; 106 | } 107 | } 108 | 109 | /// Field number for the "BanInfo" field. 110 | public const int BanInfoFieldNumber = 2; 111 | private global::Multiplayer.Packets.BanInformation banInfo_; 112 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 113 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 114 | public global::Multiplayer.Packets.BanInformation BanInfo { 115 | get { return banInfo_; } 116 | set { 117 | banInfo_ = value; 118 | } 119 | } 120 | 121 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 122 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 123 | public override bool Equals(object other) { 124 | return Equals(other as AuthResponse); 125 | } 126 | 127 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 128 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 129 | public bool Equals(AuthResponse other) { 130 | if (ReferenceEquals(other, null)) { 131 | return false; 132 | } 133 | if (ReferenceEquals(other, this)) { 134 | return true; 135 | } 136 | if (Type != other.Type) return false; 137 | if (!object.Equals(BanInfo, other.BanInfo)) return false; 138 | return Equals(_unknownFields, other._unknownFields); 139 | } 140 | 141 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 142 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 143 | public override int GetHashCode() { 144 | int hash = 1; 145 | if (Type != global::Multiplayer.Packets.ResponseType.Good) hash ^= Type.GetHashCode(); 146 | if (banInfo_ != null) hash ^= BanInfo.GetHashCode(); 147 | if (_unknownFields != null) { 148 | hash ^= _unknownFields.GetHashCode(); 149 | } 150 | return hash; 151 | } 152 | 153 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 154 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 155 | public override string ToString() { 156 | return pb::JsonFormatter.ToDiagnosticString(this); 157 | } 158 | 159 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 160 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 161 | public void WriteTo(pb::CodedOutputStream output) { 162 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 163 | output.WriteRawMessage(this); 164 | #else 165 | if (Type != global::Multiplayer.Packets.ResponseType.Good) { 166 | output.WriteRawTag(8); 167 | output.WriteEnum((int) Type); 168 | } 169 | if (banInfo_ != null) { 170 | output.WriteRawTag(18); 171 | output.WriteMessage(BanInfo); 172 | } 173 | if (_unknownFields != null) { 174 | _unknownFields.WriteTo(output); 175 | } 176 | #endif 177 | } 178 | 179 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 180 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 181 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 182 | void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { 183 | if (Type != global::Multiplayer.Packets.ResponseType.Good) { 184 | output.WriteRawTag(8); 185 | output.WriteEnum((int) Type); 186 | } 187 | if (banInfo_ != null) { 188 | output.WriteRawTag(18); 189 | output.WriteMessage(BanInfo); 190 | } 191 | if (_unknownFields != null) { 192 | _unknownFields.WriteTo(ref output); 193 | } 194 | } 195 | #endif 196 | 197 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 198 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 199 | public int CalculateSize() { 200 | int size = 0; 201 | if (Type != global::Multiplayer.Packets.ResponseType.Good) { 202 | size += 1 + pb::CodedOutputStream.ComputeEnumSize((int) Type); 203 | } 204 | if (banInfo_ != null) { 205 | size += 1 + pb::CodedOutputStream.ComputeMessageSize(BanInfo); 206 | } 207 | if (_unknownFields != null) { 208 | size += _unknownFields.CalculateSize(); 209 | } 210 | return size; 211 | } 212 | 213 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 214 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 215 | public void MergeFrom(AuthResponse other) { 216 | if (other == null) { 217 | return; 218 | } 219 | if (other.Type != global::Multiplayer.Packets.ResponseType.Good) { 220 | Type = other.Type; 221 | } 222 | if (other.banInfo_ != null) { 223 | if (banInfo_ == null) { 224 | BanInfo = new global::Multiplayer.Packets.BanInformation(); 225 | } 226 | BanInfo.MergeFrom(other.BanInfo); 227 | } 228 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 229 | } 230 | 231 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 232 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 233 | public void MergeFrom(pb::CodedInputStream input) { 234 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 235 | input.ReadRawMessage(this); 236 | #else 237 | uint tag; 238 | while ((tag = input.ReadTag()) != 0) { 239 | switch(tag) { 240 | default: 241 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 242 | break; 243 | case 8: { 244 | Type = (global::Multiplayer.Packets.ResponseType) input.ReadEnum(); 245 | break; 246 | } 247 | case 18: { 248 | if (banInfo_ == null) { 249 | BanInfo = new global::Multiplayer.Packets.BanInformation(); 250 | } 251 | input.ReadMessage(BanInfo); 252 | break; 253 | } 254 | } 255 | } 256 | #endif 257 | } 258 | 259 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 260 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 261 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 262 | void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { 263 | uint tag; 264 | while ((tag = input.ReadTag()) != 0) { 265 | switch(tag) { 266 | default: 267 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); 268 | break; 269 | case 8: { 270 | Type = (global::Multiplayer.Packets.ResponseType) input.ReadEnum(); 271 | break; 272 | } 273 | case 18: { 274 | if (banInfo_ == null) { 275 | BanInfo = new global::Multiplayer.Packets.BanInformation(); 276 | } 277 | input.ReadMessage(BanInfo); 278 | break; 279 | } 280 | } 281 | } 282 | } 283 | #endif 284 | 285 | } 286 | 287 | #endregion 288 | 289 | } 290 | 291 | #endregion Designer generated code 292 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/ChatMessage.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: chatMessage.proto 4 | // 5 | #pragma warning disable 1591, 0612, 3021 6 | #region Designer generated code 7 | 8 | using pb = global::Google.Protobuf; 9 | using pbc = global::Google.Protobuf.Collections; 10 | using pbr = global::Google.Protobuf.Reflection; 11 | using scg = global::System.Collections.Generic; 12 | namespace Multiplayer.Packets { 13 | 14 | /// Holder for reflection information generated from chatMessage.proto 15 | public static partial class ChatMessageReflection { 16 | 17 | #region Descriptor 18 | /// File descriptor for chatMessage.proto 19 | public static pbr::FileDescriptor Descriptor { 20 | get { return descriptor; } 21 | } 22 | private static pbr::FileDescriptor descriptor; 23 | 24 | static ChatMessageReflection() { 25 | byte[] descriptorData = global::System.Convert.FromBase64String( 26 | string.Concat( 27 | "ChFjaGF0TWVzc2FnZS5wcm90bxITTXVsdGlwbGF5ZXIuUGFja2V0cyJBCgtD", 28 | "aGF0TWVzc2FnZRIQCghVc2VybmFtZRgBIAEoCRIQCghDb250ZW50cxgCIAEo", 29 | "CRIOCgZUYXJnZXQYAyABKARiBnByb3RvMw==")); 30 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 31 | new pbr::FileDescriptor[] { }, 32 | new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { 33 | new pbr::GeneratedClrTypeInfo(typeof(global::Multiplayer.Packets.ChatMessage), global::Multiplayer.Packets.ChatMessage.Parser, new[]{ "Username", "Contents", "Target" }, null, null, null, null) 34 | })); 35 | } 36 | #endregion 37 | 38 | } 39 | #region Messages 40 | public sealed partial class ChatMessage : pb::IMessage 41 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 42 | , pb::IBufferMessage 43 | #endif 44 | { 45 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new ChatMessage()); 46 | private pb::UnknownFieldSet _unknownFields; 47 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 48 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 49 | public static pb::MessageParser Parser { get { return _parser; } } 50 | 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 52 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 53 | public static pbr::MessageDescriptor Descriptor { 54 | get { return global::Multiplayer.Packets.ChatMessageReflection.Descriptor.MessageTypes[0]; } 55 | } 56 | 57 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 58 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 59 | pbr::MessageDescriptor pb::IMessage.Descriptor { 60 | get { return Descriptor; } 61 | } 62 | 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 64 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 65 | public ChatMessage() { 66 | OnConstruction(); 67 | } 68 | 69 | partial void OnConstruction(); 70 | 71 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 72 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 73 | public ChatMessage(ChatMessage other) : this() { 74 | username_ = other.username_; 75 | contents_ = other.contents_; 76 | target_ = other.target_; 77 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 78 | } 79 | 80 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 81 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 82 | public ChatMessage Clone() { 83 | return new ChatMessage(this); 84 | } 85 | 86 | /// Field number for the "Username" field. 87 | public const int UsernameFieldNumber = 1; 88 | private string username_ = ""; 89 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 90 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 91 | public string Username { 92 | get { return username_; } 93 | set { 94 | username_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 95 | } 96 | } 97 | 98 | /// Field number for the "Contents" field. 99 | public const int ContentsFieldNumber = 2; 100 | private string contents_ = ""; 101 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 102 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 103 | public string Contents { 104 | get { return contents_; } 105 | set { 106 | contents_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 107 | } 108 | } 109 | 110 | /// Field number for the "Target" field. 111 | public const int TargetFieldNumber = 3; 112 | private ulong target_; 113 | /// 114 | /// -1 if everyone, else use user ID 115 | /// 116 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 117 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 118 | public ulong Target { 119 | get { return target_; } 120 | set { 121 | target_ = value; 122 | } 123 | } 124 | 125 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 126 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 127 | public override bool Equals(object other) { 128 | return Equals(other as ChatMessage); 129 | } 130 | 131 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 132 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 133 | public bool Equals(ChatMessage other) { 134 | if (ReferenceEquals(other, null)) { 135 | return false; 136 | } 137 | if (ReferenceEquals(other, this)) { 138 | return true; 139 | } 140 | if (Username != other.Username) return false; 141 | if (Contents != other.Contents) return false; 142 | if (Target != other.Target) return false; 143 | return Equals(_unknownFields, other._unknownFields); 144 | } 145 | 146 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 147 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 148 | public override int GetHashCode() { 149 | int hash = 1; 150 | if (Username.Length != 0) hash ^= Username.GetHashCode(); 151 | if (Contents.Length != 0) hash ^= Contents.GetHashCode(); 152 | if (Target != 0UL) hash ^= Target.GetHashCode(); 153 | if (_unknownFields != null) { 154 | hash ^= _unknownFields.GetHashCode(); 155 | } 156 | return hash; 157 | } 158 | 159 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 160 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 161 | public override string ToString() { 162 | return pb::JsonFormatter.ToDiagnosticString(this); 163 | } 164 | 165 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 166 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 167 | public void WriteTo(pb::CodedOutputStream output) { 168 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 169 | output.WriteRawMessage(this); 170 | #else 171 | if (Username.Length != 0) { 172 | output.WriteRawTag(10); 173 | output.WriteString(Username); 174 | } 175 | if (Contents.Length != 0) { 176 | output.WriteRawTag(18); 177 | output.WriteString(Contents); 178 | } 179 | if (Target != 0UL) { 180 | output.WriteRawTag(24); 181 | output.WriteUInt64(Target); 182 | } 183 | if (_unknownFields != null) { 184 | _unknownFields.WriteTo(output); 185 | } 186 | #endif 187 | } 188 | 189 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 190 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 191 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 192 | void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { 193 | if (Username.Length != 0) { 194 | output.WriteRawTag(10); 195 | output.WriteString(Username); 196 | } 197 | if (Contents.Length != 0) { 198 | output.WriteRawTag(18); 199 | output.WriteString(Contents); 200 | } 201 | if (Target != 0UL) { 202 | output.WriteRawTag(24); 203 | output.WriteUInt64(Target); 204 | } 205 | if (_unknownFields != null) { 206 | _unknownFields.WriteTo(ref output); 207 | } 208 | } 209 | #endif 210 | 211 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 212 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 213 | public int CalculateSize() { 214 | int size = 0; 215 | if (Username.Length != 0) { 216 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Username); 217 | } 218 | if (Contents.Length != 0) { 219 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Contents); 220 | } 221 | if (Target != 0UL) { 222 | size += 1 + pb::CodedOutputStream.ComputeUInt64Size(Target); 223 | } 224 | if (_unknownFields != null) { 225 | size += _unknownFields.CalculateSize(); 226 | } 227 | return size; 228 | } 229 | 230 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 231 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 232 | public void MergeFrom(ChatMessage other) { 233 | if (other == null) { 234 | return; 235 | } 236 | if (other.Username.Length != 0) { 237 | Username = other.Username; 238 | } 239 | if (other.Contents.Length != 0) { 240 | Contents = other.Contents; 241 | } 242 | if (other.Target != 0UL) { 243 | Target = other.Target; 244 | } 245 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 246 | } 247 | 248 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 249 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 250 | public void MergeFrom(pb::CodedInputStream input) { 251 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 252 | input.ReadRawMessage(this); 253 | #else 254 | uint tag; 255 | while ((tag = input.ReadTag()) != 0) { 256 | switch(tag) { 257 | default: 258 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 259 | break; 260 | case 10: { 261 | Username = input.ReadString(); 262 | break; 263 | } 264 | case 18: { 265 | Contents = input.ReadString(); 266 | break; 267 | } 268 | case 24: { 269 | Target = input.ReadUInt64(); 270 | break; 271 | } 272 | } 273 | } 274 | #endif 275 | } 276 | 277 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 278 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 279 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 280 | void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { 281 | uint tag; 282 | while ((tag = input.ReadTag()) != 0) { 283 | switch(tag) { 284 | default: 285 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); 286 | break; 287 | case 10: { 288 | Username = input.ReadString(); 289 | break; 290 | } 291 | case 18: { 292 | Contents = input.ReadString(); 293 | break; 294 | } 295 | case 24: { 296 | Target = input.ReadUInt64(); 297 | break; 298 | } 299 | } 300 | } 301 | } 302 | #endif 303 | 304 | } 305 | 306 | #endregion 307 | 308 | } 309 | 310 | #endregion Designer generated code 311 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/Handshake.cs: -------------------------------------------------------------------------------- 1 | // 2 | // Generated by the protocol buffer compiler. DO NOT EDIT! 3 | // source: handshake.proto 4 | // 5 | #pragma warning disable 1591, 0612, 3021 6 | #region Designer generated code 7 | 8 | using pb = global::Google.Protobuf; 9 | using pbc = global::Google.Protobuf.Collections; 10 | using pbr = global::Google.Protobuf.Reflection; 11 | using scg = global::System.Collections.Generic; 12 | namespace Multiplayer.Packets { 13 | 14 | /// Holder for reflection information generated from handshake.proto 15 | public static partial class HandshakeReflection { 16 | 17 | #region Descriptor 18 | /// File descriptor for handshake.proto 19 | public static pbr::FileDescriptor Descriptor { 20 | get { return descriptor; } 21 | } 22 | private static pbr::FileDescriptor descriptor; 23 | 24 | static HandshakeReflection() { 25 | byte[] descriptorData = global::System.Convert.FromBase64String( 26 | string.Concat( 27 | "Cg9oYW5kc2hha2UucHJvdG8SE011bHRpcGxheWVyLlBhY2tldHMiSwoJSGFu", 28 | "ZHNoYWtlEhAKCHVzZXJuYW1lGAEgASgJEg4KBnNlcnZlchgCIAEoCBIKCgJp", 29 | "ZBgDIAEoBBIQCghwYXNzd29yZBgEIAEoCWIGcHJvdG8z")); 30 | descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, 31 | new pbr::FileDescriptor[] { }, 32 | new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { 33 | new pbr::GeneratedClrTypeInfo(typeof(global::Multiplayer.Packets.Handshake), global::Multiplayer.Packets.Handshake.Parser, new[]{ "Username", "Server", "Id", "Password" }, null, null, null, null) 34 | })); 35 | } 36 | #endregion 37 | 38 | } 39 | #region Messages 40 | public sealed partial class Handshake : pb::IMessage 41 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 42 | , pb::IBufferMessage 43 | #endif 44 | { 45 | private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new Handshake()); 46 | private pb::UnknownFieldSet _unknownFields; 47 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 48 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 49 | public static pb::MessageParser Parser { get { return _parser; } } 50 | 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 52 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 53 | public static pbr::MessageDescriptor Descriptor { 54 | get { return global::Multiplayer.Packets.HandshakeReflection.Descriptor.MessageTypes[0]; } 55 | } 56 | 57 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 58 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 59 | pbr::MessageDescriptor pb::IMessage.Descriptor { 60 | get { return Descriptor; } 61 | } 62 | 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 64 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 65 | public Handshake() { 66 | OnConstruction(); 67 | } 68 | 69 | partial void OnConstruction(); 70 | 71 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 72 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 73 | public Handshake(Handshake other) : this() { 74 | username_ = other.username_; 75 | server_ = other.server_; 76 | id_ = other.id_; 77 | password_ = other.password_; 78 | _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); 79 | } 80 | 81 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 82 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 83 | public Handshake Clone() { 84 | return new Handshake(this); 85 | } 86 | 87 | /// Field number for the "username" field. 88 | public const int UsernameFieldNumber = 1; 89 | private string username_ = ""; 90 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 91 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 92 | public string Username { 93 | get { return username_; } 94 | set { 95 | username_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 96 | } 97 | } 98 | 99 | /// Field number for the "server" field. 100 | public const int ServerFieldNumber = 2; 101 | private bool server_; 102 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 103 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 104 | public bool Server { 105 | get { return server_; } 106 | set { 107 | server_ = value; 108 | } 109 | } 110 | 111 | /// Field number for the "id" field. 112 | public const int IdFieldNumber = 3; 113 | private ulong id_; 114 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 115 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 116 | public ulong Id { 117 | get { return id_; } 118 | set { 119 | id_ = value; 120 | } 121 | } 122 | 123 | /// Field number for the "password" field. 124 | public const int PasswordFieldNumber = 4; 125 | private string password_ = ""; 126 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 127 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 128 | public string Password { 129 | get { return password_; } 130 | set { 131 | password_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); 132 | } 133 | } 134 | 135 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 136 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 137 | public override bool Equals(object other) { 138 | return Equals(other as Handshake); 139 | } 140 | 141 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 142 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 143 | public bool Equals(Handshake other) { 144 | if (ReferenceEquals(other, null)) { 145 | return false; 146 | } 147 | if (ReferenceEquals(other, this)) { 148 | return true; 149 | } 150 | if (Username != other.Username) return false; 151 | if (Server != other.Server) return false; 152 | if (Id != other.Id) return false; 153 | if (Password != other.Password) return false; 154 | return Equals(_unknownFields, other._unknownFields); 155 | } 156 | 157 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 158 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 159 | public override int GetHashCode() { 160 | int hash = 1; 161 | if (Username.Length != 0) hash ^= Username.GetHashCode(); 162 | if (Server != false) hash ^= Server.GetHashCode(); 163 | if (Id != 0UL) hash ^= Id.GetHashCode(); 164 | if (Password.Length != 0) hash ^= Password.GetHashCode(); 165 | if (_unknownFields != null) { 166 | hash ^= _unknownFields.GetHashCode(); 167 | } 168 | return hash; 169 | } 170 | 171 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 172 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 173 | public override string ToString() { 174 | return pb::JsonFormatter.ToDiagnosticString(this); 175 | } 176 | 177 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 178 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 179 | public void WriteTo(pb::CodedOutputStream output) { 180 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 181 | output.WriteRawMessage(this); 182 | #else 183 | if (Username.Length != 0) { 184 | output.WriteRawTag(10); 185 | output.WriteString(Username); 186 | } 187 | if (Server != false) { 188 | output.WriteRawTag(16); 189 | output.WriteBool(Server); 190 | } 191 | if (Id != 0UL) { 192 | output.WriteRawTag(24); 193 | output.WriteUInt64(Id); 194 | } 195 | if (Password.Length != 0) { 196 | output.WriteRawTag(34); 197 | output.WriteString(Password); 198 | } 199 | if (_unknownFields != null) { 200 | _unknownFields.WriteTo(output); 201 | } 202 | #endif 203 | } 204 | 205 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 206 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 207 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 208 | void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) { 209 | if (Username.Length != 0) { 210 | output.WriteRawTag(10); 211 | output.WriteString(Username); 212 | } 213 | if (Server != false) { 214 | output.WriteRawTag(16); 215 | output.WriteBool(Server); 216 | } 217 | if (Id != 0UL) { 218 | output.WriteRawTag(24); 219 | output.WriteUInt64(Id); 220 | } 221 | if (Password.Length != 0) { 222 | output.WriteRawTag(34); 223 | output.WriteString(Password); 224 | } 225 | if (_unknownFields != null) { 226 | _unknownFields.WriteTo(ref output); 227 | } 228 | } 229 | #endif 230 | 231 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 232 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 233 | public int CalculateSize() { 234 | int size = 0; 235 | if (Username.Length != 0) { 236 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Username); 237 | } 238 | if (Server != false) { 239 | size += 1 + 1; 240 | } 241 | if (Id != 0UL) { 242 | size += 1 + pb::CodedOutputStream.ComputeUInt64Size(Id); 243 | } 244 | if (Password.Length != 0) { 245 | size += 1 + pb::CodedOutputStream.ComputeStringSize(Password); 246 | } 247 | if (_unknownFields != null) { 248 | size += _unknownFields.CalculateSize(); 249 | } 250 | return size; 251 | } 252 | 253 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 254 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 255 | public void MergeFrom(Handshake other) { 256 | if (other == null) { 257 | return; 258 | } 259 | if (other.Username.Length != 0) { 260 | Username = other.Username; 261 | } 262 | if (other.Server != false) { 263 | Server = other.Server; 264 | } 265 | if (other.Id != 0UL) { 266 | Id = other.Id; 267 | } 268 | if (other.Password.Length != 0) { 269 | Password = other.Password; 270 | } 271 | _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); 272 | } 273 | 274 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 275 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 276 | public void MergeFrom(pb::CodedInputStream input) { 277 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 278 | input.ReadRawMessage(this); 279 | #else 280 | uint tag; 281 | while ((tag = input.ReadTag()) != 0) { 282 | switch(tag) { 283 | default: 284 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); 285 | break; 286 | case 10: { 287 | Username = input.ReadString(); 288 | break; 289 | } 290 | case 16: { 291 | Server = input.ReadBool(); 292 | break; 293 | } 294 | case 24: { 295 | Id = input.ReadUInt64(); 296 | break; 297 | } 298 | case 34: { 299 | Password = input.ReadString(); 300 | break; 301 | } 302 | } 303 | } 304 | #endif 305 | } 306 | 307 | #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE 308 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute] 309 | [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] 310 | void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) { 311 | uint tag; 312 | while ((tag = input.ReadTag()) != 0) { 313 | switch(tag) { 314 | default: 315 | _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input); 316 | break; 317 | case 10: { 318 | Username = input.ReadString(); 319 | break; 320 | } 321 | case 16: { 322 | Server = input.ReadBool(); 323 | break; 324 | } 325 | case 24: { 326 | Id = input.ReadUInt64(); 327 | break; 328 | } 329 | case 34: { 330 | Password = input.ReadString(); 331 | break; 332 | } 333 | } 334 | } 335 | } 336 | #endif 337 | 338 | } 339 | 340 | #endregion 341 | 342 | } 343 | 344 | #endregion Designer generated code 345 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/generatePackets.ps1: -------------------------------------------------------------------------------- 1 | protoc -I proto --csharp_out=. ./proto/*.proto 2 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/proto/authResponse.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package Multiplayer.Packets; 3 | 4 | import "moderation.proto"; 5 | 6 | enum ResponseType { 7 | GOOD = 0; 8 | BAD = 1; 9 | BANNED = 2; 10 | } 11 | 12 | message AuthResponse { 13 | ResponseType Type = 1; 14 | optional BanInformation BanInfo = 2; 15 | } 16 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/proto/chatMessage.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package Multiplayer.Packets; 3 | 4 | message ChatMessage { 5 | string Username = 1; 6 | string Contents = 2; 7 | // -1 if everyone, else use user ID 8 | uint64 Target = 3; 9 | } 10 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/proto/gamePacket.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package Multiplayer.Packets; 3 | 4 | import "chatMessage.proto"; 5 | import "handshake.proto"; 6 | import "authResponse.proto"; 7 | import "moderation.proto"; 8 | 9 | message GamePacket { 10 | 11 | //variant 1) any packet = 1; 12 | //variant 2) 13 | uint64 reciever = 1; 14 | 15 | oneof packet { 16 | Handshake handshake = 2; 17 | ChatMessage chatMessage = 3; 18 | AuthResponse authResponse = 4; 19 | BanInformation banInformation = 5; 20 | KickInformation kickInformation = 6; 21 | } 22 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/proto/handshake.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package Multiplayer.Packets; 3 | 4 | message Handshake { 5 | string username = 1; 6 | bool server = 2; 7 | uint64 id = 3; 8 | string password = 4; 9 | } 10 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Packets/proto/moderation.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package Multiplayer.Packets; 3 | 4 | message BanInformation { 5 | string Reason = 1; 6 | int64 ExpiryDate = 2; 7 | } 8 | 9 | message KickInformation { 10 | string Reason = 1; 11 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/SIMM.cs: -------------------------------------------------------------------------------- 1 | // #pragma warning disable IDE1006 // Naming Styles 2 | // using System; 3 | // using System.Collections.Generic; 4 | // using System.Linq; 5 | // using System.Text; 6 | // using System.Threading.Tasks; 7 | // 8 | // namespace Multiplayer.Networking 9 | // { 10 | // public class SIMM 11 | // { 12 | // public class Constants 13 | // { 14 | // public class Stamps 15 | // { 16 | // 17 | // public int created { get; set; } 18 | // 19 | // public int lastLogin { get; set; } 20 | // } 21 | // public class Account 22 | // { 23 | // public string username { get; set; } 24 | // public string token { get; set; } 25 | // public string email { get; set; } 26 | // public string password { get; set; } 27 | // public Stamps stamps { get; set; } 28 | // public string steam { get; set; } 29 | // public string discord { get; set; } 30 | // public string avatar { get; set; } 31 | // } 32 | // } 33 | // } 34 | // } 35 | // #pragma warning restore IDE1006 // Naming Styles 36 | 37 | // NO CLUE WHAT THIS IS LOL? -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/GameServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Facepunch.Steamworks; 3 | using Facepunch.Steamworks.Data; 4 | using Multiplayer.Networking.Shared; 5 | using Multiplayer.Networking.Shared.Managers; 6 | using Multiplayer.Shared; 7 | 8 | namespace Multiplayer.Networking.Server 9 | { 10 | public class GameServer : IDisposable 11 | { 12 | 13 | #region Events 14 | 15 | // TODO: Expand these events with relevant infomation. 16 | public event EventHandler ServerStarted; 17 | public event EventHandler ServerStopped; 18 | public event EventHandler ClientConnected; 19 | public event EventHandler ClientDisconnected; 20 | 21 | #endregion 22 | 23 | public GameServerSocket SocketManager { get; private set; } 24 | public ServerInfo ServerInfo { get; set; } 25 | public UserManager UserManager { get; private set; } 26 | public ILogger Logger { get; private set; } 27 | public void Start(ServerInfo serverInfo) 28 | { 29 | this.ServerInfo = serverInfo; 30 | this.UserManager = new UserManager(); 31 | this.SocketManager = SteamNetworkingSockets.CreateNormalSocket(NetAddress.AnyIp(serverInfo.Port)); 32 | this.SocketManager.Logger = Logger; 33 | Logger.Info("Server Started!"); 34 | OnServerStarted(); 35 | } 36 | 37 | 38 | public GameServer(ILogger logger) 39 | { 40 | this.Logger = logger; 41 | RegisterManager.LoadInstances(logger, null, this); 42 | } 43 | 44 | 45 | 46 | public void Dispose() 47 | { 48 | SocketManager?.Dispose(); 49 | } 50 | 51 | public void Stop() 52 | { 53 | this.SocketManager.Close(); 54 | Logger.Info("Server Stopped!"); 55 | // ServerStopped.Invoke(this, EventArgs.Empty); 56 | } 57 | 58 | public void OnServerStarted() 59 | { 60 | ServerStarted?.Invoke(this, EventArgs.Empty); 61 | } 62 | 63 | public void OnServerStopped() 64 | { 65 | ServerStopped?.Invoke(this, EventArgs.Empty); 66 | } 67 | 68 | public void OnClientConnected() 69 | { 70 | ClientConnected?.Invoke(this, EventArgs.Empty); 71 | } 72 | 73 | public void OnClientDisconnected() 74 | { 75 | ClientDisconnected?.Invoke(this, EventArgs.Empty); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/GameServerSocket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Runtime.InteropServices; 6 | using Facepunch.Steamworks; 7 | using Facepunch.Steamworks.Data; 8 | using Google.Protobuf; 9 | using Multiplayer.Networking.Shared; 10 | using Multiplayer.Networking.Shared.Managers; 11 | using Multiplayer.Packets; 12 | using Multiplayer.Shared; 13 | 14 | namespace Multiplayer.Networking.Server 15 | { 16 | public class GameServerSocket : SocketManager, IDisposable 17 | { 18 | 19 | public GameServer Parent { get; set; } 20 | 21 | public ILogger Logger; 22 | 23 | private readonly ArrayPool bufferPool = ArrayPool.Create(); 24 | 25 | private readonly List packetHandlers; 26 | 27 | public GameServerSocket() 28 | {} 29 | 30 | public override void OnConnectionChanged(Connection connection, ConnectionInfo info) 31 | { 32 | base.OnConnectionChanged(connection, info); 33 | } 34 | 35 | 36 | public override void OnConnected(Connection connection, ConnectionInfo info) 37 | { 38 | Logger.Debug($"Connection: {connection.ToString()}, connectionInfo: {info.Address}"); 39 | Logger.Debug($"Connected!"); 40 | Parent.OnClientConnected(); 41 | } 42 | 43 | public override void OnConnecting(Connection connection, ConnectionInfo info) 44 | { 45 | Logger.Debug($"Connection: {connection.ToString()}, connectionInfo: {info.Address}"); 46 | var result = connection.Accept(); 47 | Logger.Debug($"Accepted"); 48 | } 49 | 50 | public override void OnDisconnected(Connection connection, ConnectionInfo info) 51 | { 52 | Logger.Debug($"Connection: {connection.ToString()}, connectionInfo: {info.Address}"); 53 | var result = connection.Close(); 54 | Logger.Debug($"Disconnected and Closed"); 55 | Parent.OnClientDisconnected(); 56 | } 57 | 58 | public override void OnMessage(Connection connection, NetIdentity identity, IntPtr data, int size, long messageNum, long recvTime, int channel) 59 | { 60 | Logger.Debug($"Connection: {connection}, identity: {identity.Address}"); 61 | Logger.Debug($"On Message, size: {size}, messageNum: {messageNum}"); 62 | 63 | // TODO this should have a reasonable size 64 | if (size > 10 * 1024 * 1024) // 10kb 65 | { 66 | Logger.Warn("Discarding large packet"); 67 | return; 68 | } 69 | 70 | var buffer = this.bufferPool.Rent(size); 71 | Marshal.Copy(data, buffer, 0, size); 72 | 73 | var gamePacket = GamePacket.Parser.ParseFrom(buffer, 0, size); 74 | 75 | if (gamePacket.PacketCase == GamePacket.PacketOneofCase.None) return; 76 | if (RegisterManager.ServerPacketHandlersCache.TryGetValue(gamePacket.PacketCase, out var handlers)) 77 | { 78 | foreach (IPacketHandler handler in handlers) 79 | { 80 | handler.HandlePacket(null, gamePacket); 81 | } 82 | } 83 | 84 | // TODO: Move this switch into packet handlers. 85 | 86 | // switch (gamePacket.PacketCase) 87 | // { 88 | // case GamePacket.PacketOneofCase.None: // error 89 | // break; 90 | // case GamePacket.PacketOneofCase.Handshake: 91 | // var handshakePacket = gamePacket.Handshake; 92 | // if (Parent.ServerInfo.HasPassword) 93 | // { 94 | // if (Parent.ServerInfo.Password != handshakePacket.Password) 95 | // { 96 | // // Invalid Password 97 | // Send(new AuthResponse() 98 | // { 99 | // Type = ResponseType.Bad 100 | // }, connection); 101 | // return; 102 | // } 103 | // 104 | // // Password correct. 105 | // GameUser user = new GameUser() 106 | // { 107 | // Name = handshakePacket.Username, 108 | // Role = UserRole.Guest, 109 | // Id = handshakePacket.Id 110 | // }; 111 | // 112 | // Parent.UserManager.GetOrAddUser(user); 113 | // 114 | // BanInformation? banned = Parent.UserManager.CheckBanned(user); 115 | // 116 | // if (banned != null) 117 | // { 118 | // Send(new AuthResponse() 119 | // { 120 | // Type = ResponseType.Banned, 121 | // BanInfo = banned 122 | // }, connection); 123 | // return; 124 | // } 125 | // 126 | // Send(new Handshake() 127 | // { 128 | // Server = true 129 | // }, connection); 130 | // } 131 | // 132 | // break; 133 | // case GamePacket.PacketOneofCase.ChatMessage: 134 | // break; 135 | // } 136 | } 137 | 138 | public unsafe void Send(T message, Connection connection) where T : IMessage 139 | { 140 | //TODO implement a sending queue and a background task/thread 141 | Logger.Debug($"Sending message of type {typeof(T)}: {message}"); 142 | using (var serializationStream = new MemoryStream()) 143 | { 144 | message.WriteTo(serializationStream); 145 | var buffer = serializationStream.GetBuffer(); 146 | var messageSize = message.CalculateSize(); 147 | fixed (byte* p = buffer) 148 | { 149 | connection.SendMessage((IntPtr)p, messageSize); 150 | } 151 | serializationStream.Position = 0; 152 | } 153 | 154 | Logger.Debug($"Message sent"); 155 | 156 | } 157 | 158 | public unsafe void SendAll(T message) where T : IMessage 159 | { 160 | //TODO implement a sending queue and a background task/thread 161 | Logger.Debug($"Sending message of type {typeof(T)}: {message}"); 162 | using (var serializationStream = new MemoryStream()) 163 | { 164 | message.WriteTo(serializationStream); 165 | var buffer = serializationStream.GetBuffer(); 166 | var messageSize = message.CalculateSize(); 167 | fixed (byte* p = buffer) 168 | { 169 | foreach (var connection in Socket.Manager.Connected) 170 | { 171 | connection.SendMessage((IntPtr)p, messageSize); 172 | } 173 | } 174 | serializationStream.Position = 0; 175 | } 176 | 177 | Logger.Debug($"Message sent"); 178 | 179 | } 180 | 181 | public void Dispose() 182 | { 183 | try 184 | { 185 | foreach (var connection in Connected) 186 | { 187 | connection.Close(); 188 | } 189 | this.Close(); 190 | } 191 | catch (Exception ex) 192 | { 193 | Logger.Error(ex); 194 | } 195 | Parent.OnServerStopped(); 196 | } 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/Handlers/ChatHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | using Facepunch.Steamworks.Data; 3 | using Multiplayer.Networking.Shared.Managers; 4 | using Multiplayer.Packets; 5 | 6 | namespace Multiplayer.Networking.Server.Handlers 7 | { 8 | [RegisterManager(RegisterType.Server, GamePacket.PacketOneofCase.ChatMessage)] 9 | public class ChatHandler : ServerPacketHandler 10 | { 11 | public ChatHandler(GameServer server) : base(server) { } 12 | 13 | public override void HandlePacket(Connection sender, ChatMessage packet) 14 | { 15 | if (BigInteger.Compare(packet.Target, BigInteger.Zero) < 0) 16 | { 17 | // For everyone. 18 | server.SocketManager.SendAll(packet); 19 | } 20 | else 21 | { 22 | // For specific user. 23 | //TODO: send to specific user 24 | // server.SocketManager.Send(); 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/Handlers/HandshakeHandler.cs: -------------------------------------------------------------------------------- 1 | using Facepunch.Steamworks.Data; 2 | using Multiplayer.Networking.Shared.Managers; 3 | using Multiplayer.Packets; 4 | 5 | namespace Multiplayer.Networking.Server.Handlers 6 | { 7 | [RegisterManager(RegisterType.Server, GamePacket.PacketOneofCase.Handshake)] 8 | public class HandshakeHandler : ServerPacketHandler 9 | { 10 | public HandshakeHandler(GameServer server) : base(server) 11 | { } 12 | 13 | public override void HandlePacket(Connection sender, Handshake packet) 14 | { 15 | 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/Handlers/ServerPacketHandler.cs: -------------------------------------------------------------------------------- 1 | using Facepunch.Steamworks.Data; 2 | using Google.Protobuf; 3 | using Multiplayer.Networking.Shared; 4 | using Multiplayer.Packets; 5 | 6 | namespace Multiplayer.Networking.Server 7 | { 8 | public abstract class ServerPacketHandler : IPacketHandler where T : IMessage 9 | { 10 | public virtual int Priority => 0; 11 | public void HandlePacket(IPacketHandler.PacketSender sender, GamePacket packet) 12 | { 13 | var fields = packet.GetType().GetFields(); 14 | foreach (var field in fields) 15 | { 16 | if (field.FieldType == typeof(T)) 17 | { 18 | HandlePacket(sender.Connection.Value, (T)field.GetValue(packet)); 19 | } 20 | } 21 | } 22 | 23 | public abstract void HandlePacket(Connection sender, T packet); 24 | 25 | protected readonly GameServer server; 26 | 27 | public ServerPacketHandler(GameServer server) 28 | { 29 | this.server = server; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/Managers/BanManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Multiplayer.Networking.Server.Managers 8 | { 9 | public class BanManager 10 | { 11 | public HashSet Bans = new HashSet(); 12 | 13 | // TODO this ban manager does not persist bans 14 | 15 | 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/ServerEvents.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Multiplayer.Networking.Shared; 3 | 4 | namespace Multiplayer.Networking 5 | { 6 | 7 | public class ClientConnectedEventArgs : EventArgs 8 | { 9 | public uint ConnectionId { get; } 10 | public ClientConnectedEventArgs(uint connectionId) 11 | { 12 | this.ConnectionId = connectionId; 13 | } 14 | } 15 | 16 | public class ClientDisconnectedEventArgs : EventArgs 17 | { 18 | public uint ConnectionId { get; } 19 | public ClientDisconnectedEventArgs(uint connectionId) 20 | { 21 | this.ConnectionId = connectionId; 22 | } 23 | } 24 | 25 | public class UserConnectedEventArgs : EventArgs 26 | { 27 | public GameUser User { get; } 28 | public UserConnectedEventArgs(GameUser user) 29 | { 30 | this.User = user; 31 | } 32 | } 33 | 34 | public class UserDisconnectedEventArgs : EventArgs 35 | { 36 | public GameUser User { get; } 37 | public UserDisconnectedEventArgs(GameUser user) 38 | { 39 | this.User = user; 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Server/ServerInfo.cs: -------------------------------------------------------------------------------- 1 | using Multiplayer.Networking.Shared; 2 | 3 | namespace Multiplayer.Networking 4 | { 5 | public class ServerInfo 6 | { 7 | public string Name { get; set; } 8 | public string Description { get; set; } 9 | public string Password { get; set; } 10 | public bool HasPassword => string.IsNullOrEmpty(this.Password); 11 | public UserRole DefaultRole { get; set; } 12 | public ushort Port { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Shared/GameUser.cs: -------------------------------------------------------------------------------- 1 | namespace Multiplayer.Networking.Shared 2 | { 3 | public class GameUser 4 | { 5 | public ulong Id { get; set; } 6 | public string Name { get; set; } 7 | 8 | public UserRole Role { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Shared/IPacketHandler.cs: -------------------------------------------------------------------------------- 1 | using Facepunch.Steamworks.Data; 2 | using Multiplayer.Packets; 3 | 4 | namespace Multiplayer.Networking.Shared 5 | { 6 | 7 | 8 | public interface IPacketHandler 9 | { 10 | 11 | public class PacketSender 12 | { 13 | public Connection? Connection; 14 | public GameUser? User; 15 | } 16 | 17 | int Priority { get; } 18 | void HandlePacket(PacketSender sender, GamePacket packet); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Shared/Managers/RegisterManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using Multiplayer.Networking.Client; 5 | using Multiplayer.Networking.Server; 6 | using Multiplayer.Packets; 7 | using Multiplayer.Shared; 8 | 9 | namespace Multiplayer.Networking.Shared.Managers 10 | { 11 | public enum RegisterType 12 | { 13 | Client, 14 | Server 15 | } 16 | 17 | public class RegisterManager : Attribute 18 | { 19 | public static readonly Dictionary> ClientPacketHandlersCache = new Dictionary>(); 20 | public static readonly Dictionary> ServerPacketHandlersCache = new Dictionary>(); 21 | 22 | private RegisterType type; 23 | private GamePacket.PacketOneofCase catcher; 24 | 25 | public virtual RegisterType Type { get => this.type; set => type = value; } 26 | public virtual GamePacket.PacketOneofCase Catcher { get => this.catcher; set => catcher = value; } 27 | 28 | public RegisterManager(RegisterType type, GamePacket.PacketOneofCase catcher) 29 | { 30 | this.type = type; 31 | this.catcher = catcher; 32 | } 33 | 34 | public static void LoadInstances(ILogger logger, GameClient? client, GameServer? server) 35 | { 36 | logger.Info("Loading Handlers @ " + (client != null ? "Client" : "Server")); 37 | foreach(var t in Assembly.GetCallingAssembly().GetTypes()) 38 | { 39 | var f = t.GetCustomAttributes(typeof(RegisterManager), true); 40 | 41 | if (f.Length <= 0) 42 | continue; 43 | 44 | logger.Info("Found class with RegisterManager: " + t.Name); 45 | 46 | var manager = (RegisterManager)f[0]; 47 | 48 | logger.Info("RegisterManager at " + t.Name + " for " + manager.type + " catches " + manager.Catcher); 49 | 50 | var ctor = t.GetConstructor(new[] { manager.type == RegisterType.Client ? typeof(GameClient) : typeof(GameServer) }); 51 | if (ctor == null) 52 | break; 53 | 54 | switch (manager.type) 55 | { 56 | case RegisterType.Client when client != null: 57 | { 58 | if (ClientPacketHandlersCache.TryGetValue(manager.Catcher, out var arr)) 59 | { 60 | arr.Add((IPacketHandler) ctor.Invoke(new object[] { client })); 61 | } 62 | else 63 | { 64 | ClientPacketHandlersCache.Add(manager.Catcher, new List { (IPacketHandler)ctor.Invoke(new object[] { client }) }); 65 | } 66 | break; 67 | } 68 | case RegisterType.Server when server != null: 69 | { 70 | if (ServerPacketHandlersCache.TryGetValue(manager.Catcher, out var arr)) 71 | { 72 | arr.Add((IPacketHandler) ctor.Invoke(new object[] { server })); 73 | } 74 | else 75 | { 76 | ServerPacketHandlersCache.Add(manager.Catcher, new List { (IPacketHandler)ctor.Invoke(new object[] { server }) }); 77 | } 78 | break; 79 | } 80 | default: 81 | break; 82 | } 83 | 84 | logger.Info("Registered Catcher - " + manager.Catcher ); 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Shared/Managers/UserManager.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Multiplayer.Packets; 6 | 7 | // TODO: Rewrite 8 | 9 | namespace Multiplayer.Networking.Shared 10 | { 11 | public enum UserRole 12 | { 13 | Host, 14 | Admin, 15 | Player, 16 | Guest 17 | } 18 | 19 | public interface IUserManager 20 | { 21 | event EventHandler UserAdded; 22 | event EventHandler UserRemoved; 23 | 24 | bool HasUser(ulong userId); 25 | GameUser GetUser(ulong userId); 26 | GameUser GetOrAddUser(GameUser user); 27 | void RemoveUser(ulong userId); 28 | void RemoveUser(GameUser user); 29 | 30 | int Count(); 31 | 32 | void Clear(); 33 | } 34 | 35 | /// 36 | /// The UserManager holds information for all known users on a server 37 | /// 38 | public class UserManager : IUserManager 39 | { 40 | /*public event EventHandler UserAdded; 41 | public event EventHandler UserRemoved; 42 | public Dictionary Users { get; set; } 43 | public int GuestCount = 0; 44 | public bool GetUser(string username, out User user) 45 | { 46 | return Users.TryGetValue(username, out user); 47 | } 48 | public void RemoveUser(int ID) 49 | { 50 | 51 | } 52 | public void RemoveUser(string username) 53 | { 54 | bool exists = Users.TryGetValue(username, out User user); 55 | if (!exists) return; 56 | UserRemoved.Invoke(this, user); 57 | Users.Remove(username); 58 | } 59 | public void AddUser(User user) { 60 | string username = ""; 61 | if(!user.IsLoggedIn) 62 | { 63 | GuestCount++; 64 | // TODO fixme 65 | //username = $"{user.Username} + {GuestCount}"; 66 | } 67 | Users.Add(username, user); 68 | UserAdded.Invoke(this, user); 69 | }*/ 70 | 71 | #region Events 72 | public event EventHandler UserAdded; 73 | public event EventHandler UserRemoved; 74 | #endregion 75 | 76 | private readonly Dictionary userIdToUser = new Dictionary(); 77 | private readonly Dictionary bannedUsers = new Dictionary(); 78 | 79 | public UserManager() 80 | { 81 | // TODO implement persistance 82 | } 83 | 84 | public bool HasUser(ulong userId) 85 | { 86 | return this.userIdToUser.ContainsKey(userId); 87 | } 88 | 89 | public GameUser GetUser(ulong userId) 90 | { 91 | if (this.userIdToUser.TryGetValue(userId, out var user)) 92 | return user; 93 | return null; 94 | } 95 | 96 | public GameUser GetOrAddUser(GameUser user) 97 | { 98 | if (!this.userIdToUser.TryGetValue(user.Id, out var oldUser)) 99 | { 100 | this.userIdToUser.Add(user.Id, user); 101 | 102 | this.UserAdded?.Invoke(this, new UserAddedEventArgs(user)); 103 | return user; 104 | } 105 | 106 | return oldUser; 107 | } 108 | 109 | 110 | public BanInformation? CheckBanned(GameUser user) 111 | { 112 | return this.bannedUsers.TryGetValue(user.Id, out var banInfo) ? banInfo : null; 113 | } 114 | 115 | public void RemoveUser(ulong userId) 116 | { 117 | if (!this.userIdToUser.TryGetValue(userId, out var removeUser)) 118 | return; 119 | 120 | this.userIdToUser.Remove(removeUser.Id); 121 | 122 | this.UserRemoved?.Invoke(this, new UserRemovedEventArgs(removeUser)); 123 | } 124 | 125 | public void RemoveUser(GameUser user) 126 | { 127 | this.RemoveUser(user.Id); 128 | } 129 | 130 | public int Count() 131 | { 132 | return userIdToUser.Count; 133 | } 134 | 135 | public void Clear() 136 | { 137 | foreach(var removedUser in this.userIdToUser.Select(x => x.Value).ToList()) 138 | { 139 | this.userIdToUser.Remove(removedUser.Id); 140 | this.UserRemoved?.Invoke(this, new UserRemovedEventArgs(removedUser)); 141 | } 142 | } 143 | } 144 | 145 | public class UserAddedEventArgs : EventArgs 146 | { 147 | 148 | public UserAddedEventArgs(GameUser user) 149 | { 150 | User = user; 151 | } 152 | 153 | public GameUser User { get; } 154 | } 155 | 156 | public class UserRemovedEventArgs : EventArgs 157 | { 158 | 159 | public UserRemovedEventArgs(GameUser user) 160 | { 161 | User = user; 162 | } 163 | 164 | public GameUser User { get; } 165 | } 166 | } -------------------------------------------------------------------------------- /Multiplayer.Networking/Utility/PacketSerializer.cs: -------------------------------------------------------------------------------- 1 | // THIS CAN BE REPLACED WITH PROTOBUF 2 | 3 | 4 | // //#define PACKET_ID 5 | // 6 | // using System; 7 | // using System.Collections.Generic; 8 | // using System.IO; 9 | // using System.Linq; 10 | // using System.Runtime.Serialization.Formatters.Binary; 11 | // //using MessagePack; 12 | // using Multiplayer.Networking; 13 | // using Packets; 14 | // 15 | // namespace Multiplayer.Networking.Utility 16 | // { 17 | // public class PacketSerializer 18 | // //#if PACKET_ID 19 | // : IDisposable 20 | // //#endif 21 | // { 22 | // //public MessagePackSerializerOptions Options { get; } 23 | // 24 | // private readonly MemoryStream packetStream = new MemoryStream(); 25 | // private readonly BinaryFormatter formatter = new BinaryFormatter(); 26 | // 27 | // #if PACKET_ID 28 | // private readonly MemoryStream packetStream = new MemoryStream(); 29 | // /// 30 | // /// This is the central place to register known packet types 31 | // /// 32 | // private static readonly IReadOnlyDictionary PacketMapping = new Dictionary() 33 | // { 34 | // { 1, typeof(Handshake) }, 35 | // { 2, typeof(Disconnect) }, 36 | // { 3, typeof(WelcomeUser) }, 37 | // { 4, typeof(ChatMessage) }, 38 | // { 5, typeof(PrivateChatMessage) }, 39 | // }; 40 | // 41 | // private static readonly IReadOnlyDictionary ReversePacketMapping; 42 | // 43 | // static PacketSerializer() { 44 | // ReversePacketMapping = PacketMapping.ToDictionary(x => x.Value, x => x.Key); 45 | // } 46 | // #endif 47 | // public PacketSerializer() 48 | // { 49 | // // see https://github.com/neuecc/MessagePack-CSharp#security 50 | // /*var resolver = MessagePack.Resolvers.CompositeResolver.Create( 51 | // new[] { MessagePack.Formatters.TypelessFormatter.Instance }, 52 | // new[] { MessagePack.Resolvers.StandardResolver.Instance });*/ 53 | // 54 | // /*this.Options = MessagePackSerializer.Typeless.DefaultOptions//MessagePackSerializerOptions.Standard 55 | // .WithOmitAssemblyVersion(true) 56 | // //.WithResolver(resolver) 57 | // //.WithCompression(MessagePackCompression.Lz4Block) 58 | // .WithSecurity(MessagePackSecurity.UntrustedData);*/ 59 | // 60 | // this.formatter.FilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Low; 61 | // this.formatter.TypeFormat = System.Runtime.Serialization.Formatters.FormatterTypeStyle.TypesWhenNeeded; 62 | // } 63 | // 64 | // public byte[] SerializePacket(TPacket packet) 65 | // where TPacket : IPacket 66 | // { 67 | // // TODO maybe we need locking here 68 | // #if PACKET_ID 69 | // //if (!ReversePacketMapping.TryGetValue(typeof(TPacket), out var packetId)) 70 | // // return null; 71 | // // this is a variant with a dedicated packetId 72 | // this.packetStream.WriteByte(packetId); 73 | // MessagePackSerializer.Serialize(this.packetStream, packet, this.Options); 74 | // 75 | // // create a byte[] copy of the stream contents 76 | // var serializedPacket = this.packetStream.ToArray(); 77 | // this.packetStream.SetLength(0L); 78 | // 79 | // return serializedPacket; 80 | // #else 81 | // formatter.Serialize(packetStream, packet); 82 | // //TODO this is evil! dont do it this way! use an array pool or something like that <.< 83 | // var bytes = packetStream.ToArray(); 84 | // packetStream.SetLength(0); 85 | // 86 | // return bytes; 87 | // //return MessagePackSerializer.Typeless.Serialize(packet, this.Options); 88 | // #endif 89 | // } 90 | // 91 | // public IPacket DeserializePacket(byte[] buffer) 92 | // { 93 | // #if PACKET_ID 94 | // var memoryBuffer = new Memory(buffer); 95 | // var packetId = memoryBuffer.Slice(0, 1).Span[0]; 96 | // var packetData = memoryBuffer.Slice(1); 97 | // 98 | // if (!PacketMapping.TryGetValue(packetId, out var packetType)) 99 | // return null; 100 | // 101 | // return MessagePackSerializer.Deserialize(packetType, packetData, this.Options) as IPacket; 102 | // #else 103 | // //return MessagePackSerializer.Typeless.Deserialize(buffer, this.Options) as IPacket; 104 | // this.packetStream.Write(buffer, 0, buffer.Length); 105 | // this.packetStream.Position = 0; 106 | // var obj = formatter.Deserialize(packetStream); 107 | // packetStream.SetLength(0); 108 | // 109 | // return obj as IPacket; 110 | // #endif 111 | // } 112 | // 113 | // //#if PACKET_ID 114 | // public void Dispose() 115 | // { 116 | // this.packetStream?.Dispose(); 117 | // } 118 | // 119 | // ~PacketSerializer() 120 | // { 121 | // this.Dispose(); 122 | // } 123 | // //#endif 124 | // } 125 | // 126 | // 127 | // } 128 | // 129 | // /// 130 | // /// Keep this namespace SHORT as it is used in serialization 131 | // /// 132 | // // namespace Packets 133 | // // { 134 | // // public interface IPacket 135 | // // { 136 | // // ulong Sender { get; } 137 | // // } 138 | // // 139 | // // //[MessagePackObject] 140 | // // [Serializable] 141 | // // public class Handshake : IPacket 142 | // // { 143 | // // //[Key(0)] 144 | // // public ulong Sender { get; } 145 | // // //[Key(1)] 146 | // // public string UserName { get; set; } 147 | // // //[Key(2)] 148 | // // public string Password { get; set; } 149 | // // 150 | // // public Handshake(ulong userId, string userName, string password = null) 151 | // // { 152 | // // this.Sender = userId; 153 | // // this.UserName = userName; 154 | // // this.Password = password; 155 | // // } 156 | // // 157 | // // // TODO in the future we need to use the steam networking library to verify the user identity via steam tokens 158 | // // } 159 | // // 160 | // // //[MessagePackObject] 161 | // // [Serializable] 162 | // // public class WelcomeUser : IPacket 163 | // // { 164 | // // //[Key(0)] 165 | // // public ulong Sender { get; } 166 | // // //[Key(1)] 167 | // // public string UserName { get; set; } 168 | // // 169 | // // public WelcomeUser(ulong userId, string userName) 170 | // // { 171 | // // this.Sender = userId; 172 | // // this.UserName = userName; 173 | // // } 174 | // // } 175 | // // 176 | // // 177 | // // 178 | // // /// 179 | // // /// this enforces a clean disconnect 180 | // // /// 181 | // // //[MessagePackObject] 182 | // // [Serializable] 183 | // // public class Disconnect : IPacket 184 | // // { 185 | // // //[Key(0)] 186 | // // public ulong Sender { get; } 187 | // // //[Key(1)] 188 | // // public DisconnectReason Reason { get; } 189 | // // 190 | // // public Disconnect(ulong sender, DisconnectReason reason) 191 | // // { 192 | // // this.Sender = sender; 193 | // // this.Reason = reason; 194 | // // } 195 | // // } 196 | // // 197 | // // //[MessagePackObject] 198 | // // [Serializable] 199 | // // public class ChatMessage : IPacket 200 | // // { 201 | // // //[Key(0)] 202 | // // public ulong Sender { get; } 203 | // // //[Key(1)] 204 | // // public string Message { get; } 205 | // // 206 | // // public ChatMessage(ulong sender, string message) 207 | // // { 208 | // // this.Sender = sender; 209 | // // this.Message = message; 210 | // // } 211 | // // } 212 | // // 213 | // // //[MessagePackObject] 214 | // // [Serializable] 215 | // // public class PrivateChatMessage : IPacket 216 | // // { 217 | // // //[Key(0)] 218 | // // public ulong Sender { get; } 219 | // // //[Key(1)] 220 | // // public ulong Receiver { get; } 221 | // // //[Key(2)] 222 | // // public string Message { get; } 223 | // // 224 | // // public PrivateChatMessage(ulong sender, ulong receiver, string message) 225 | // // { 226 | // // this.Sender = sender; 227 | // // this.Receiver = receiver; 228 | // // this.Message = message; 229 | // // } 230 | // // } 231 | // // } 232 | -------------------------------------------------------------------------------- /Multiplayer.Networking/Utility/SteamHelper.cs: -------------------------------------------------------------------------------- 1 | using Facepunch.Steamworks; 2 | 3 | namespace Multiplayer.Networking.Utility 4 | { 5 | public static class SteamHelper 6 | { 7 | public static bool Initialized { get; private set; } = false; 8 | private const int Appid = 362620; 9 | 10 | public static void Enable() 11 | { 12 | if (Initialized) 13 | return; 14 | SteamClient.Init(Appid); 15 | Initialized = true; 16 | } 17 | 18 | public static void Disable() 19 | { 20 | if (!Initialized) 21 | return; 22 | SteamClient.Shutdown(); 23 | 24 | Initialized = false; 25 | } 26 | 27 | public static void Update() 28 | { 29 | SteamClient.RunCallbacks(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Multiplayer.Shared/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Multiplayer.Shared 9 | { 10 | /// 11 | /// shared logging interface 12 | /// 13 | public interface ILogger 14 | { 15 | void Log(LogType logType, object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0); 16 | void Debug(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0); 17 | void Info(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0); 18 | void Warn(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0); 19 | void Error(object obj, Exception ex = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int sourceLineNumber = 0); 20 | } 21 | 22 | public enum LogType 23 | { 24 | Info = 0, 25 | Debug = 2, 26 | Warn = 3, 27 | Error = 4 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Multiplayer.Shared/Multiplayer.Shared.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0;net472 5 | full 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 |
8 |

Software Inc Multiplayer Mod

9 |

A multiplayer mod for the game "Software Inc"

10 |
11 | 12 |

Contents

13 | 14 | - Installation 15 | - [Manual Installation (Windows only, mainly for contributors)](#manual-installation) 16 | - How it works 17 | - Submitting a bug report/feature request. 18 | - Contributing 19 | 20 | ## Installation 21 | 22 | 29 | 30 | ###### Manual Installation (Windows/Contributors/Developers only) 31 | 32 | 1. Head to the _"Utilities"_ folder and run __as administrator__ the batch file called _"setSoftwareIncFolder.bat"_; 33 | 2. It will ask you to copy and paste the game's directory installation path (usually something like _"A:\SteamLibrary\steamapps\common\Software Inc"_, to find it quickly, right click on the game on Steam->Manage->Browse Local Files); 34 | 3. The batch file will then execute the following batch command to set an Environment variable called _softwareincfolder_: 35 | ```batch 36 | setx softwareincfolder "%pathyoutcopypasted" /m 37 | ``` 38 | 4. All the references in the various projects inside the solution use the environment variable to point at the game root folder in order to avoid having to overwrite those references path at every push from a different person. 39 | 5. There are also post-build events that automatically move the required DLL to the proper game's folders so everything you need to do is just build and play the game to see the mod! 40 | 41 | To remove the environment variable use the following batch command in an administrator prompt window: 42 | ```batch 43 | REG delete HKCU\Environment /F /V softwareincfolder 44 | ``` 45 | And then a full computer restart for the changes to take effect. 46 | 47 | ## How it works 48 | 49 | The multiplayer mod is based on the Peer To Peer system currently, with dedicated servers coming soon. 50 | 51 | First, the client connects to the server and sends their "GameWorld", which is their company, their stocks, etc. 52 | Secondly, the client will recieve a collection of GameWorlds from the other clients and merges them with theirs, showing other players companies and their stocks etc. 53 | Lastly, any changes will be sent to the server to be sent to the other clients and vice versa. 54 | 55 | ## Bug Reports and Feature Requests 56 | 57 | Anyone can submit a bug report via the issues tab on GitHub, please provide as much infomation as possible. 58 | Same for feature requests. 59 | 60 | ## Contributing 61 | 62 | See CONTRIBUTING.md 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /Utilities/setSoftwareIncFolder.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/Utilities/setSoftwareIncFolder.bat -------------------------------------------------------------------------------- /create-binaries.ps1: -------------------------------------------------------------------------------- 1 | Try { 2 | New-Item -Path . -Name "installer-binaries" -ItemType "directory" 3 | New-Item -Path "./installer-binaries/" -Name "mf" -ItemType "directory" -Force 4 | New-Item -Path "./installer-binaries/" -Name "manage" -ItemType "directory" -Force 5 | # Copy assets etc. 6 | Copy-Item -Path ".\Assets\" -Destination ".\installer-binaries\mf\Assets\" -Recurse -Force 7 | Copy-Item -Path ".\Localization\" -Destination ".\installer-binaries\mf\" -Recurse -Force 8 | # .\Multiplayer.Core\bin\Debug\Multiplayer.Core.dll 9 | Copy-Item -Path ".\Multiplayer.Core\bin\Debug\Multiplayer.Core.dll" -Destination ".\installer-binaries\mf\" -Recurse 10 | Copy-Item -Path ".\Multiplayer.Networking\bin\Debug\Multiplayer.Networking.dll" -Destination ".\installer-binaries\manage\" -Recurse -Force 11 | Copy-Item -Path ".\Multiplayer.Extensions\bin\Debug\Multiplayer.Extensions.dll" -Destination ".\installer-binaries\manage\" -Recurse -Force 12 | Copy-Item -Path ".\Multiplayer.Debugging\bin\Debug\Multiplayer.Debugging.dll" -Destination ".\installer-binaries\manage\" -Recurse -Force 13 | # .\References 14 | Copy-Item -Path ".\References\Telepathy.dll" -Destination ".\installer-binaries\manage\" -Force 15 | Copy-Item -Path ".\References\Newtonsoft.Json.dll" -Destination ".\installer-binaries\manage\" -Force 16 | Compress-Archive -Path ".\installer-binaries\*" -DestinationPath ".\installer-binaries.zip" -Force 17 | } 18 | Catch { 19 | echo "Couldn't make installer-binaries:" 20 | echo $_ 21 | } -------------------------------------------------------------------------------- /lib/net46/Facepunch.Steamworks.Win64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/lib/net46/Facepunch.Steamworks.Win64.dll -------------------------------------------------------------------------------- /lib/net46/steam_api64_new.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/lib/net46/steam_api64_new.dll -------------------------------------------------------------------------------- /lib/netstandard2.0/Facepunch.Steamworks.Win64.deps.json: -------------------------------------------------------------------------------- 1 | { 2 | "runtimeTarget": { 3 | "name": ".NETStandard,Version=v2.0/", 4 | "signature": "" 5 | }, 6 | "compilationOptions": {}, 7 | "targets": { 8 | ".NETStandard,Version=v2.0": {}, 9 | ".NETStandard,Version=v2.0/": { 10 | "Facepunch.Steamworks.Win64/1.0.0": { 11 | "dependencies": { 12 | "NETStandard.Library": "2.0.3" 13 | }, 14 | "runtime": { 15 | "Facepunch.Steamworks.Win64.dll": {} 16 | } 17 | }, 18 | "Microsoft.NETCore.Platforms/1.1.0": {}, 19 | "NETStandard.Library/2.0.3": { 20 | "dependencies": { 21 | "Microsoft.NETCore.Platforms": "1.1.0" 22 | } 23 | } 24 | } 25 | }, 26 | "libraries": { 27 | "Facepunch.Steamworks.Win64/1.0.0": { 28 | "type": "project", 29 | "serviceable": false, 30 | "sha512": "" 31 | }, 32 | "Microsoft.NETCore.Platforms/1.1.0": { 33 | "type": "package", 34 | "serviceable": true, 35 | "sha512": "sha512-a/iSwnRZb+LHFk49hQOyThh/ZNC3vsbZsF65XwQIb863qF6msmhdQtxGXFL28Ob2NsCz/drEj28BJd/YPpLRBg==", 36 | "path": "microsoft.netcore.platforms/1.1.0", 37 | "hashPath": "microsoft.netcore.platforms.1.1.0.nupkg.sha512" 38 | }, 39 | "NETStandard.Library/2.0.3": { 40 | "type": "package", 41 | "serviceable": true, 42 | "sha512": "sha512-st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", 43 | "path": "netstandard.library/2.0.3", 44 | "hashPath": "netstandard.library.2.0.3.nupkg.sha512" 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /lib/netstandard2.0/Facepunch.Steamworks.Win64.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/lib/netstandard2.0/Facepunch.Steamworks.Win64.dll -------------------------------------------------------------------------------- /lib/netstandard2.0/steam_api64_new.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/software-inc-multiplayer/software-inc-multiplayer/ec7c1019aaf98779ee2fdf9147f165f3cea7a491/lib/netstandard2.0/steam_api64_new.dll -------------------------------------------------------------------------------- /swinc.multiplayer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29609.76 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Core", "Multiplayer.Core\Multiplayer.Core.csproj", "{13F254D3-469C-4D57-BB72-7D8E881A8C64}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Debugging", "Multiplayer.Debugging\Multiplayer.Debugging.csproj", "{3BA2B696-5228-429F-8FFF-FBF6CA7A5303}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Extensions", "Multiplayer.Extensions\Multiplayer.Extensions.csproj", "{816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Networking", "Multiplayer.Networking\Multiplayer.Networking.csproj", "{19CD273F-A601-45ED-AB6D-6E61F47A074C}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{F6DAFE06-4C3F-413E-BD60-C0D1F11D37C5}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Networking.Test", "Multiplayer.Networking.Test\Multiplayer.Networking.Test.csproj", "{1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}" 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Source", "Source", "{759A2496-5BF7-4FE1-B6EF-B13656B6E0F0}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Multiplayer.Shared", "Multiplayer.Shared\Multiplayer.Shared.csproj", "{9DF5CA61-BCA1-47A5-A99F-559F91458F10}" 21 | EndProject 22 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Utilities", "Utilities", "{77920CDB-B8AE-456D-8730-A8F44BDE2E8F}" 23 | ProjectSection(SolutionItems) = preProject 24 | Utilities\setSoftwareIncFolder.bat = Utilities\setSoftwareIncFolder.bat 25 | EndProjectSection 26 | EndProject 27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "RootContents", "RootContents", "{83734F5F-2D5A-4F5A-885E-3840027369E4}" 28 | ProjectSection(SolutionItems) = preProject 29 | README.md = README.md 30 | EndProjectSection 31 | EndProject 32 | Global 33 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 34 | Debug|Any CPU = Debug|Any CPU 35 | Release|Any CPU = Release|Any CPU 36 | UNIT_TEST|Any CPU = UNIT_TEST|Any CPU 37 | EndGlobalSection 38 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 39 | {13F254D3-469C-4D57-BB72-7D8E881A8C64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 40 | {13F254D3-469C-4D57-BB72-7D8E881A8C64}.Debug|Any CPU.Build.0 = Debug|Any CPU 41 | {13F254D3-469C-4D57-BB72-7D8E881A8C64}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {13F254D3-469C-4D57-BB72-7D8E881A8C64}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {13F254D3-469C-4D57-BB72-7D8E881A8C64}.UNIT_TEST|Any CPU.ActiveCfg = Release|Any CPU 44 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.UNIT_TEST|Any CPU.ActiveCfg = Debug|Any CPU 49 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303}.UNIT_TEST|Any CPU.Build.0 = Debug|Any CPU 50 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.UNIT_TEST|Any CPU.ActiveCfg = Debug|Any CPU 55 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8}.UNIT_TEST|Any CPU.Build.0 = Debug|Any CPU 56 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.UNIT_TEST|Any CPU.ActiveCfg = UNIT_TEST|Any CPU 61 | {19CD273F-A601-45ED-AB6D-6E61F47A074C}.UNIT_TEST|Any CPU.Build.0 = UNIT_TEST|Any CPU 62 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.UNIT_TEST|Any CPU.ActiveCfg = Debug|Any CPU 67 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706}.UNIT_TEST|Any CPU.Build.0 = Debug|Any CPU 68 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.UNIT_TEST|Any CPU.ActiveCfg = Debug|Any CPU 73 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10}.UNIT_TEST|Any CPU.Build.0 = Debug|Any CPU 74 | EndGlobalSection 75 | GlobalSection(SolutionProperties) = preSolution 76 | HideSolutionNode = FALSE 77 | EndGlobalSection 78 | GlobalSection(NestedProjects) = preSolution 79 | {13F254D3-469C-4D57-BB72-7D8E881A8C64} = {759A2496-5BF7-4FE1-B6EF-B13656B6E0F0} 80 | {3BA2B696-5228-429F-8FFF-FBF6CA7A5303} = {759A2496-5BF7-4FE1-B6EF-B13656B6E0F0} 81 | {816FE3F3-50EF-44A7-A54A-F2EE0346E9F8} = {759A2496-5BF7-4FE1-B6EF-B13656B6E0F0} 82 | {19CD273F-A601-45ED-AB6D-6E61F47A074C} = {759A2496-5BF7-4FE1-B6EF-B13656B6E0F0} 83 | {1B4A9A9F-00CD-4EEF-AC1A-09E0CD73B706} = {F6DAFE06-4C3F-413E-BD60-C0D1F11D37C5} 84 | {9DF5CA61-BCA1-47A5-A99F-559F91458F10} = {759A2496-5BF7-4FE1-B6EF-B13656B6E0F0} 85 | EndGlobalSection 86 | GlobalSection(ExtensibilityGlobals) = postSolution 87 | SolutionGuid = {94CED7EE-2A12-4D13-8C7B-985FA62B3630} 88 | EndGlobalSection 89 | EndGlobal 90 | --------------------------------------------------------------------------------