├── .editorconfig ├── .gitattributes ├── .gitignore ├── .gitmodules ├── Jenkinsfile ├── LICENSE.md ├── ProjectEarthServerAPI.sln ├── ProjectEarthServerAPI ├── Authentication │ └── GenoaAuthenticationHandler.cs ├── Controllers │ ├── AdventureController.cs │ ├── CdnTileController.cs │ ├── CraftingController.cs │ ├── LocationController.cs │ ├── LocatorController.cs │ ├── MultiplayerController.cs │ ├── PlayerController.cs │ ├── ResourcePackController.cs │ ├── SigninController.cs │ ├── SmeltingController.cs │ └── TappablesController.cs ├── Models │ ├── CDN │ │ └── ResourcePackResponse.cs │ ├── Features │ │ ├── CatalogResponse.cs │ │ ├── CraftingRequest.cs │ │ ├── CraftingResponse.cs │ │ ├── Item.cs │ │ ├── JournalResponse.cs │ │ ├── ProductCatalogResponse.cs │ │ ├── Recipes.cs │ │ ├── SmeltingRequest.cs │ │ ├── SmeltingResponse.cs │ │ └── UtilityBlocksResponse.cs │ ├── Login │ │ ├── FeaturesResponse.cs │ │ ├── LocatorResponse.cs │ │ ├── SettingsResponse.cs │ │ ├── SigninRequest.cs │ │ └── SigninResponse.cs │ ├── Multiplayer │ │ ├── Adventure │ │ │ ├── AdventureRequestResult.cs │ │ │ └── PlayerAdventureRequest.cs │ │ ├── Buildplate │ │ │ ├── BuildplateListResponse.cs │ │ │ ├── BuildplateServerRequest.cs │ │ │ ├── BuildplateServerResponse.cs │ │ │ └── PlayerBuildplateList.cs │ │ ├── MultiplayerInventoryResponse.cs │ │ ├── ServerAuthInformation.cs │ │ └── ServerCommandRequest.cs │ ├── Player │ │ ├── BoostsResponse.cs │ │ ├── ChallengesResponse.cs │ │ ├── InventoryResponse.cs │ │ ├── LocationResponse.cs │ │ ├── ProfileResponse.cs │ │ ├── RubyResponse.cs │ │ ├── ScrollsResponse.cs │ │ ├── TappableRequest.cs │ │ ├── TappableResponse.cs │ │ └── TokenResponse.cs │ ├── Rewards.cs │ ├── Token.cs │ └── Updates.cs ├── Program.cs ├── ProjectEarthServerAPI.csproj ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Util │ ├── AdventureUtils.cs │ ├── BoostUtils.cs │ ├── BuildplateUtils.cs │ ├── ChallengeUtils.cs │ ├── CraftingUtils.cs │ ├── DateTimeConverter.cs │ ├── ETag.cs │ ├── GenericUtils.cs │ ├── InventoryUtils.cs │ ├── JournalUtils.cs │ ├── MultiplayerItemConverters.cs │ ├── MultiplayerUtils.cs │ ├── ProfileUtils.cs │ ├── RewardUtils.cs │ ├── RubyUtils.cs │ ├── ServerConfig.cs │ ├── SmeltingUtils.cs │ ├── StateSingleton.cs │ ├── StringToUuidConv.cs │ ├── TappableUtils.cs │ ├── Tile.cs │ ├── TokenUtils.cs │ └── UtilityBlockUtils.cs ├── appsettings.Development.json └── appsettings.json ├── README.md └── build.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories 2 | root = true 3 | 4 | # C# files 5 | [*.cs] 6 | indent_style = tab 7 | indent_size = tab 8 | tab_size = 4 9 | 10 | # New line preferences 11 | end_of_line = crlf 12 | insert_final_newline = true 13 | 14 | 15 | #### C# Coding Conventions #### 16 | 17 | # Expression-bodied members 18 | csharp_style_expression_bodied_accessors = true:silent 19 | csharp_style_expression_bodied_constructors = false:silent 20 | csharp_style_expression_bodied_indexers = true:silent 21 | csharp_style_expression_bodied_lambdas = true:silent 22 | csharp_style_expression_bodied_local_functions = false:silent 23 | csharp_style_expression_bodied_methods = false:silent 24 | csharp_style_expression_bodied_operators = false:silent 25 | csharp_style_expression_bodied_properties = true:silent 26 | 27 | # Pattern matching preferences 28 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 29 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 30 | csharp_style_prefer_not_pattern = true:suggestion 31 | csharp_style_prefer_pattern_matching = true:silent 32 | csharp_style_prefer_switch_expression = true:suggestion 33 | 34 | # Null-checking preferences 35 | csharp_style_conditional_delegate_call = true:suggestion 36 | 37 | # Code-block preferences 38 | csharp_prefer_braces = true:silent 39 | 40 | # Expression-level preferences 41 | csharp_prefer_simple_default_expression = true:suggestion 42 | csharp_style_deconstructed_variable_declaration = true:suggestion 43 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion 44 | csharp_style_inlined_variable_declaration = true:suggestion 45 | csharp_style_pattern_local_over_anonymous_function = true:suggestion 46 | csharp_style_prefer_index_operator = true:suggestion 47 | csharp_style_prefer_range_operator = true:suggestion 48 | csharp_style_throw_expression = true:suggestion 49 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion 50 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent 51 | 52 | # 'using' directive preferences 53 | csharp_using_directive_placement = outside_namespace:silent 54 | 55 | #### C# Formatting Rules #### 56 | 57 | # New line preferences 58 | csharp_new_line_before_catch = true 59 | csharp_new_line_before_else = true 60 | csharp_new_line_before_finally = true 61 | csharp_new_line_before_members_in_anonymous_types = true 62 | csharp_new_line_before_members_in_object_initializers = true 63 | csharp_new_line_before_open_brace = all 64 | csharp_new_line_between_query_expression_clauses = true 65 | 66 | # Indentation preferences 67 | csharp_indent_block_contents = true 68 | csharp_indent_braces = false 69 | csharp_indent_case_contents = true 70 | csharp_indent_case_contents_when_block = true 71 | csharp_indent_labels = no_change 72 | csharp_indent_switch_labels = true 73 | 74 | # Space preferences 75 | csharp_space_after_cast = false 76 | csharp_space_after_colon_in_inheritance_clause = true 77 | csharp_space_after_comma = true 78 | csharp_space_after_dot = false 79 | csharp_space_after_keywords_in_control_flow_statements = true 80 | csharp_space_after_semicolon_in_for_statement = true 81 | csharp_space_around_binary_operators = before_and_after 82 | csharp_space_around_declaration_statements = false 83 | csharp_space_before_colon_in_inheritance_clause = true 84 | csharp_space_before_comma = false 85 | csharp_space_before_dot = false 86 | csharp_space_before_open_square_brackets = false 87 | csharp_space_before_semicolon_in_for_statement = false 88 | csharp_space_between_empty_square_brackets = false 89 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 90 | csharp_space_between_method_call_name_and_opening_parenthesis = false 91 | csharp_space_between_method_call_parameter_list_parentheses = false 92 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 93 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 94 | csharp_space_between_method_declaration_parameter_list_parentheses = false 95 | csharp_space_between_parentheses = false 96 | csharp_space_between_square_brackets = false 97 | csharp_space_within_single_line_array_initializer_braces = false 98 | 99 | # Wrapping preferences 100 | csharp_preserve_single_line_blocks = true 101 | csharp_preserve_single_line_statements = true -------------------------------------------------------------------------------- /.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 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # Visual Studio Code dust 39 | .vscode/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUNIT 46 | *.VisualState.xml 47 | TestResult.xml 48 | 49 | # Build Results of an ATL Project 50 | [Dd]ebugPS/ 51 | [Rr]eleasePS/ 52 | dlldata.c 53 | 54 | # Benchmark Results 55 | BenchmarkDotNet.Artifacts/ 56 | 57 | # .NET Core 58 | project.lock.json 59 | project.fragment.lock.json 60 | artifacts/ 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_h.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *_wpftmp.csproj 86 | *.log 87 | *.vspscc 88 | *.vssscc 89 | .builds 90 | *.pidb 91 | *.svclog 92 | *.scc 93 | 94 | # Chutzpah Test files 95 | _Chutzpah* 96 | 97 | # Visual C++ cache files 98 | ipch/ 99 | *.aps 100 | *.ncb 101 | *.opendb 102 | *.opensdf 103 | *.sdf 104 | *.cachefile 105 | *.VC.db 106 | *.VC.VC.opendb 107 | 108 | # Visual Studio profiler 109 | *.psess 110 | *.vsp 111 | *.vspx 112 | *.sap 113 | 114 | # Visual Studio Trace Files 115 | *.e2e 116 | 117 | # TFS 2012 Local Workspace 118 | $tf/ 119 | 120 | # Guidance Automation Toolkit 121 | *.gpState 122 | 123 | # ReSharper is a .NET coding add-in 124 | _ReSharper*/ 125 | *.[Rr]e[Ss]harper 126 | *.DotSettings.user 127 | 128 | # JustCode is a .NET coding add-in 129 | .JustCode 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # The packages folder can be ignored because of Package Restore 189 | **/[Pp]ackages/* 190 | # except build/, which is used as an MSBuild target. 191 | !**/[Pp]ackages/build/ 192 | # Uncomment if necessary however generally it will be regenerated when needed 193 | #!**/[Pp]ackages/repositories.config 194 | # NuGet v3's project.json files produces more ignorable files 195 | *.nuget.props 196 | *.nuget.targets 197 | 198 | # Microsoft Azure Build Output 199 | csx/ 200 | *.build.csdef 201 | 202 | # Microsoft Azure Emulator 203 | ecf/ 204 | rcf/ 205 | 206 | # Windows Store app package directories and files 207 | AppPackages/ 208 | BundleArtifacts/ 209 | Package.StoreAssociation.xml 210 | _pkginfo.txt 211 | *.appx 212 | 213 | # Visual Studio cache files 214 | # files ending in .cache can be ignored 215 | *.[Cc]ache 216 | # but keep track of directories ending in .cache 217 | !?*.[Cc]ache/ 218 | 219 | # Others 220 | ClientBin/ 221 | ~$* 222 | *~ 223 | *.dbmdl 224 | *.dbproj.schemaview 225 | *.jfm 226 | *.pfx 227 | *.publishsettings 228 | orleans.codegen.cs 229 | 230 | # Including strong name files can present a security risk 231 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 232 | #*.snk 233 | 234 | # Since there are multiple workflows, uncomment next line to ignore bower_components 235 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 236 | #bower_components/ 237 | 238 | # RIA/Silverlight projects 239 | Generated_Code/ 240 | 241 | # Backup & report files from converting an old project file 242 | # to a newer Visual Studio version. Backup files are not needed, 243 | # because we have git ;-) 244 | _UpgradeReport_Files/ 245 | Backup*/ 246 | UpgradeLog*.XML 247 | UpgradeLog*.htm 248 | ServiceFabricBackup/ 249 | *.rptproj.bak 250 | 251 | # SQL Server files 252 | *.mdf 253 | *.ldf 254 | *.ndf 255 | 256 | # Business Intelligence projects 257 | *.rdl.data 258 | *.bim.layout 259 | *.bim_*.settings 260 | *.rptproj.rsuser 261 | *- Backup*.rdl 262 | 263 | # Microsoft Fakes 264 | FakesAssemblies/ 265 | 266 | # GhostDoc plugin setting file 267 | *.GhostDoc.xml 268 | 269 | # Node.js Tools for Visual Studio 270 | .ntvs_analysis.dat 271 | node_modules/ 272 | 273 | # Visual Studio 6 build log 274 | *.plg 275 | 276 | # Visual Studio 6 workspace options file 277 | *.opt 278 | 279 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 280 | *.vbw 281 | 282 | # Visual Studio LightSwitch build output 283 | **/*.HTMLClient/GeneratedArtifacts 284 | **/*.DesktopClient/GeneratedArtifacts 285 | **/*.DesktopClient/ModelManifest.xml 286 | **/*.Server/GeneratedArtifacts 287 | **/*.Server/ModelManifest.xml 288 | _Pvt_Extensions 289 | 290 | # Paket dependency manager 291 | .paket/paket.exe 292 | paket-files/ 293 | 294 | # FAKE - F# Make 295 | .fake/ 296 | 297 | # JetBrains Rider 298 | .idea/ 299 | *.sln.iml 300 | 301 | # CodeRush personal settings 302 | .cr/personal 303 | 304 | # Python Tools for Visual Studio (PTVS) 305 | __pycache__/ 306 | *.pyc 307 | 308 | # Cake - Uncomment if you are using it 309 | # tools/** 310 | # !tools/packages.config 311 | 312 | # Tabs Studio 313 | *.tss 314 | 315 | # Telerik's JustMock configuration file 316 | *.jmconfig 317 | 318 | # BizTalk build output 319 | *.btp.cs 320 | *.btm.cs 321 | *.odx.cs 322 | *.xsd.cs 323 | 324 | # OpenCover UI analysis results 325 | OpenCover/ 326 | 327 | # Azure Stream Analytics local run output 328 | ASALocalRun/ 329 | 330 | # MSBuild Binary and Structured Log 331 | *.binlog 332 | 333 | # NVidia Nsight GPU debugger configuration file 334 | *.nvuser 335 | 336 | # MFractors (Xamarin productivity tool) working folder 337 | .mfractor/ 338 | 339 | # Local History for Visual Studio 340 | .localhistory/ 341 | 342 | # BeatPulse healthcheck temp database 343 | healthchecksdb 344 | /ProjectEarthServerAPI/Properties/ServiceDependencies/projectearthdev - Web Deploy/profile.arm.json 345 | /ProjectEarthServerAPI/logs 346 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ProjectEarthServerAPI/data"] 2 | path = ProjectEarthServerAPI/data 3 | url = https://github.com/Project-Earth-Team/ApiData.git 4 | -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline{ 2 | agent any 3 | tools { 4 | dotnetsdk '.NET 5' 5 | } 6 | 7 | stages { 8 | stage('Build'){ 9 | steps{ 10 | sh 'dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained false' 11 | } 12 | post{ 13 | success{ 14 | archiveArtifacts artifacts: 'ProjectEarthServerAPI/bin/Debug/net5.0/linux-x64/publish/ProjectEarthServerAPI' 15 | archiveArtifacts artifacts: 'ProjectEarthServerAPI/bin/Debug/net5.0/linux-x64/publish/ProjectEarthServerAPI.pdb' 16 | } 17 | } 18 | } 19 | } 20 | 21 | post { 22 | always { 23 | script { 24 | def changeLogSets = currentBuild.changeSets 25 | def message = "**Changes:**" 26 | 27 | if (changeLogSets.size() == 0) { 28 | message += "\n*No changes.*" 29 | } else { 30 | def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "") 31 | def count = 0; 32 | def extra = 0; 33 | for (int i = 0; i < changeLogSets.size(); i++) { 34 | def entries = changeLogSets[i].items 35 | for (int j = 0; j < entries.length; j++) { 36 | if (count <= 10) { 37 | def entry = entries[j] 38 | def commitId = entry.commitId.substring(0, 6) 39 | message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}" 40 | count++ 41 | } else { 42 | extra++; 43 | } 44 | } 45 | } 46 | 47 | if (extra != 0) { 48 | message += "\n - ${extra} more commits" 49 | } 50 | } 51 | 52 | env.changes = message 53 | } 54 | deleteDir() 55 | withCredentials([string(credentialsId: 'projectearth-discord-webhook', variable: 'DISCORD_WEBHOOK')]) { 56 | discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.rtm516.co.uk/job/ProjectEarth/job/Api/job/master/)", footer: 'rtm516\'s Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK 57 | } 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Project-Earth-Api 2 | Copyright (C) 2021 Project Earth Team 3 | 4 | This program is free software: you can redistribute it and/or modify 5 | it under the terms of the GNU Affero General Public License as published by 6 | the Free Software Foundation, either version 3 of the License, or 7 | (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU Affero General Public License for more details. 13 | 14 | You should have received a copy of the GNU Affero General Public License 15 | along with this program. If not, see . 16 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30717.126 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectEarthServerAPI", "ProjectEarthServerAPI\ProjectEarthServerAPI.csproj", "{EE1E1340-AFE0-4CEB-BA24-7B70AEB384C5}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EE1E1340-AFE0-4CEB-BA24-7B70AEB384C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EE1E1340-AFE0-4CEB-BA24-7B70AEB384C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EE1E1340-AFE0-4CEB-BA24-7B70AEB384C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EE1E1340-AFE0-4CEB-BA24-7B70AEB384C5}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {80785D1B-4FAA-4CBD-B1D6-548D49FA54E0} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Authentication/GenoaAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using System; 7 | using System.Net.Http.Headers; 8 | using System.Security.Claims; 9 | using System.Text.Encodings.Web; 10 | using System.Threading.Tasks; 11 | 12 | namespace ProjectEarthServerAPI.Authentication 13 | { 14 | public class GenoaAuthenticationHandler : AuthenticationHandler 15 | { 16 | public GenoaAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } 17 | 18 | protected override async Task HandleAuthenticateAsync() 19 | { 20 | // skip authentication if endpoint has [AllowAnonymous] attribute 21 | var endpoint = Context.GetEndpoint(); 22 | if (endpoint?.Metadata?.GetMetadata() != null) 23 | return AuthenticateResult.NoResult(); 24 | 25 | // Check if we should really authenticate 26 | if (endpoint?.Metadata?.GetMetadata() == null) 27 | return AuthenticateResult.NoResult(); 28 | 29 | if (!Request.Headers.ContainsKey("Authorization")) 30 | return AuthenticateResult.Fail("Missing Authorization Header"); 31 | 32 | string id; 33 | try 34 | { 35 | var authHeader = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]); 36 | if (authHeader.Scheme.Equals("Genoa")) 37 | { 38 | id = authHeader.Parameter; 39 | } 40 | else 41 | { 42 | return AuthenticateResult.Fail("Invalid Authorization Header"); 43 | } 44 | } 45 | catch 46 | { 47 | return AuthenticateResult.Fail("Invalid Authorization Header"); 48 | } 49 | 50 | var claims = new[] {new Claim(ClaimTypes.NameIdentifier, id),}; 51 | 52 | var identity = new ClaimsIdentity(claims, Scheme.Name); 53 | var principal = new ClaimsPrincipal(identity); 54 | var ticket = new AuthenticationTicket(principal, Scheme.Name); 55 | 56 | return AuthenticateResult.Success(ticket); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/AdventureController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Newtonsoft.Json; 8 | using ProjectEarthServerAPI.Models; 9 | using ProjectEarthServerAPI.Models.Multiplayer.Adventure; 10 | using ProjectEarthServerAPI.Util; 11 | 12 | namespace ProjectEarthServerAPI.Controllers 13 | { 14 | [Authorize] 15 | [ApiVersion("1.1")] 16 | public class AdventureScrollsController : Controller 17 | { 18 | [Route("1/api/v{version:apiVersion}/adventures/scrolls")] 19 | public ContentResult Get() 20 | { 21 | var responseobj = new ScrollsResponse(); 22 | var response = JsonConvert.SerializeObject(responseobj); 23 | return Content(response, "application/json"); 24 | } // TODO: Fixed String 25 | 26 | [Route("1/api/v{version:apiVersion}/adventures/scrolls/{crystalId}")] 27 | public async Task PostRedeemCrystal(Guid crystalId) 28 | { 29 | var playerId = User.FindFirstValue(ClaimTypes.NameIdentifier); 30 | 31 | var stream = new StreamReader(Request.Body); 32 | var body = await stream.ReadToEndAsync(); 33 | 34 | var req = JsonConvert.DeserializeObject(body); 35 | var resp = AdventureUtils.RedeemCrystal(playerId, req, crystalId); 36 | 37 | return Content(JsonConvert.SerializeObject(resp), "application/json"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/CdnTileController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using System.IO; 9 | using ProjectEarthServerAPI.Util; 10 | using ProjectEarthServerAPI.Models; 11 | 12 | namespace ProjectEarthServerAPI.Controllers 13 | { 14 | [ApiVersion("1.1")] 15 | [Route("cdn/tile/16/{_}/{tilePos1}_{tilePos2}_16.png")] 16 | [ResponseCache(Duration = 11200)] 17 | public class CdnTileController : ControllerBase 18 | { 19 | public IActionResult Get(int _, int tilePos1, int tilePos2) // _ used because we dont care :| 20 | { 21 | String targetTilePath = $"./data/tiles/16/{tilePos1}/{tilePos1}_{tilePos2}_16.png"; 22 | 23 | if (!System.IO.File.Exists(targetTilePath)) 24 | { 25 | var boo = Tile.DownloadTile(tilePos1, tilePos2, @"./data/tiles/16/"); 26 | 27 | //Lets download that lovely tile now, Shall we? 28 | if (boo == false) 29 | { 30 | return Content("hi!"); 31 | } // Error 400 on Tile download error 32 | } 33 | 34 | //String targetTilePath = $"./data/tiles/creeper_tile.png"; 35 | byte[] fileData = System.IO.File.ReadAllBytes(targetTilePath); //Namespaces 36 | var cd = new System.Net.Mime.ContentDisposition {FileName = tilePos1 + "_" + tilePos2 + "_16.png", Inline = true}; 37 | Response.Headers.Add("Content-Disposition", cd.ToString()); 38 | 39 | 40 | return File(fileData, "application/octet-stream", tilePos1 + "_" + tilePos2 + "_16.png"); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/CraftingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using ProjectEarthServerAPI.Models.Features; 8 | using ProjectEarthServerAPI.Util; 9 | using Microsoft.AspNetCore.Authorization; 10 | using System.Security.Claims; 11 | using ProjectEarthServerAPI.Models; 12 | using Serilog; 13 | 14 | namespace ProjectEarthServerAPI.Controllers 15 | { 16 | // TODO: Not done. Rewards need inventory implementation, timers for crafting process, and recipeId -> recipe time checks 17 | [Authorize] 18 | public class CraftingController : Controller 19 | { 20 | [ApiVersion("1.1")] 21 | [Route("1/api/v{version:apiVersion}/crafting/{slot}/start")] 22 | public async Task PostNewCraftingJob(int slot) 23 | { 24 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 25 | 26 | var stream = new StreamReader(Request.Body); 27 | var body = await stream.ReadToEndAsync(); 28 | 29 | var req = JsonConvert.DeserializeObject(body); 30 | 31 | var craftingJob = CraftingUtils.StartCraftingJob(authtoken, slot, req); 32 | 33 | 34 | var updateResponse = new CraftingUpdates {updates = new Updates()}; 35 | 36 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 37 | 38 | updateResponse.updates.crafting = nextStreamId; 39 | updateResponse.updates.inventory = nextStreamId; 40 | 41 | return Content(JsonConvert.SerializeObject(updateResponse), "application/json"); 42 | //return Accepted(Content(returnUpdates, "application/json")); 43 | } 44 | 45 | [ApiVersion("1.1")] 46 | [Route("1/api/v{version:apiVersion}/crafting/finish/price")] 47 | public IActionResult GetCraftingPrice(int slot) 48 | { 49 | TimeSpan remainingTime = TimeSpan.Parse(Request.Query["remainingTime"]); 50 | var returnPrice = new CraftingPriceResponse {result = new CraftingPrice {cost = 1, discount = 0, validTime = remainingTime}, updates = new Updates()}; 51 | 52 | return Content(JsonConvert.SerializeObject(returnPrice), "application/json"); 53 | } 54 | 55 | 56 | [ApiVersion("1.1")] 57 | [Route("1/api/v{version:apiVersion}/crafting/{slot}")] 58 | public IActionResult GetCraftingStatus(int slot) 59 | { 60 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 61 | 62 | var craftingStatus = CraftingUtils.GetCraftingJobInfo(authtoken, slot); 63 | 64 | return Content(JsonConvert.SerializeObject(craftingStatus), "application/json"); 65 | //return Accepted(Content(returnTokens, "application/json")); 66 | } 67 | 68 | [ApiVersion("1.1")] 69 | [Route("1/api/v{version:apiVersion}/crafting/{slot}/collectItems")] 70 | public IActionResult GetCollectCraftingItems(int slot) 71 | { 72 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 73 | 74 | var returnUpdates = CraftingUtils.FinishCraftingJob(authtoken, slot); 75 | 76 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 77 | //return Accepted(Content(returnTokens, "application/json")); 78 | } 79 | 80 | [ApiVersion("1.1")] 81 | [Route("1/api/v{version:apiVersion}/crafting/{slot}/stop")] 82 | public IActionResult GetStopCraftingJob(int slot) 83 | { 84 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 85 | 86 | var returnUpdates = CraftingUtils.CancelCraftingJob(authtoken, slot); 87 | 88 | //return Accepted(); 89 | 90 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 91 | //return Accepted(Content(returnTokens, "application/json")); 92 | } 93 | 94 | [ApiVersion("1.1")] 95 | [Route("1/api/v{version:apiVersion}/crafting/{slot}/unlock")] 96 | public IActionResult GetUnlockCraftingSlot(int slot) 97 | { 98 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 99 | 100 | var returnUpdates = CraftingUtils.UnlockCraftingSlot(authtoken, slot); 101 | 102 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/LocationController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using ProjectEarthServerAPI.Models; 5 | using Newtonsoft.Json; 6 | using ProjectEarthServerAPI.Util; 7 | using Microsoft.AspNetCore.Authorization; 8 | using ProjectEarthServerAPI.Models.Features; 9 | using Uma.Uuid; 10 | 11 | namespace ProjectEarthServerAPI.Controllers 12 | { 13 | [Authorize] 14 | [ApiVersion("1.1")] 15 | [Route("1/api/v{version:apiVersion}/locations/{latitude}/{longitude}")] 16 | public class LocationController : Controller 17 | { 18 | private static Random random = new Random(); 19 | 20 | public ContentResult Get(double latitude, double longitude) 21 | { 22 | var currentTime = DateTime.UtcNow; 23 | //Nab tile loc 24 | int[] cords = Tile.getTileForCords(latitude, longitude); 25 | List tappables = new List(); 26 | int numTappablesToSpawn = random.Next(StateSingleton.Instance.config.minTappableSpawnAmount, 27 | StateSingleton.Instance.config.maxTappableSpawnAmount); 28 | for (int i = 0; i < numTappablesToSpawn; i++) 29 | { 30 | var tappable = TappableUtils.createTappableInRadiusOfCoordinates(longitude, latitude); 31 | //add the tappable to the list 32 | tappables.Add(tappable); 33 | //add its GUID to the singleton so we can grab the correct reward pool later 34 | StateSingleton.Instance.activeTappableTypes.Add(Guid.Parse(tappable.id), tappable.icon); 35 | } 36 | 37 | 38 | //Create our final response 39 | LocationResponse.Root locationResp = new LocationResponse.Root 40 | { 41 | result = new LocationResponse.Result 42 | { 43 | killSwitchedTileIds = new List { }, //havent seen this used. Debugging thing maybe? 44 | activeLocations = tappables, 45 | }, 46 | expiration = null, 47 | continuationToken = null, 48 | updates = new Updates() 49 | }; 50 | 51 | //serialize 52 | var response = JsonConvert.SerializeObject(locationResp); 53 | //Send 54 | return Content(response, "application/json"); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/LocatorController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.Extensions.Logging; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | using ProjectEarthServerAPI.Models; 9 | using ProjectEarthServerAPI.Util; 10 | 11 | namespace ProjectEarthServerAPI.Controllers 12 | { 13 | [ApiController] 14 | [ApiVersion("1.0")] 15 | [ApiVersion("1.1")] 16 | [Route("player/environment")] 17 | public class LocatorController : Controller 18 | { 19 | private readonly ILogger _logger; 20 | 21 | public LocatorController(ILogger logger) 22 | { 23 | _logger = logger; 24 | } 25 | 26 | [HttpGet] 27 | public ContentResult Get() 28 | { 29 | string baseServerIP = StateSingleton.Instance.config.baseServerIP; 30 | 31 | LocatorResponse.Root response = new LocatorResponse.Root() 32 | { 33 | result = new LocatorResponse.Result() 34 | { 35 | serviceEnvironments = new LocatorResponse.ServiceEnvironments() 36 | { 37 | production = new LocatorResponse.Production() 38 | { 39 | playfabTitleId = "11509", 40 | serviceUri = baseServerIP, 41 | cdnUri = baseServerIP + "/cdn", 42 | //playfabTitleId = "F0DE2" //maybe make our own soon? - Mojang could kill this anytime after server sunset with no warning. 43 | } 44 | }, 45 | supportedEnvironments = new Dictionary>() {{"2020.1217.02", new List() {"production"}}} 46 | }, 47 | //updates = new LocatorResponse.Updates() 48 | }; 49 | 50 | var resp = JsonConvert.SerializeObject(response); 51 | return Content(resp, "application/json"); 52 | } 53 | } 54 | 55 | [ApiController] 56 | [ApiVersion("1.0")] 57 | [ApiVersion("1.1")] 58 | [Route("/api/v1.1/player/environment")] 59 | //:mojanktriggered: 60 | // Basically, the MCE gods decided peace was not an option 61 | // so for some reason, some devices include the /api/v1.1, and some don't. 62 | // Hence this terrible thing. 63 | public class MojankLocatorController : Controller 64 | { 65 | private readonly ILogger _logger; 66 | 67 | public MojankLocatorController(ILogger logger) 68 | { 69 | _logger = logger; 70 | } 71 | 72 | [HttpGet] 73 | public ContentResult Get() 74 | { 75 | string baseServerIP = StateSingleton.Instance.config.baseServerIP; 76 | 77 | LocatorResponse.Root response = new LocatorResponse.Root() 78 | { 79 | result = new LocatorResponse.Result() 80 | { 81 | serviceEnvironments = new LocatorResponse.ServiceEnvironments() 82 | { 83 | production = new LocatorResponse.Production() 84 | { 85 | playfabTitleId = "11509", serviceUri = baseServerIP, cdnUri = baseServerIP + "/cdn", 86 | //playfabTitleId = "F0DE2" //maybe make our own soon? - Mojang could kill this anytime after server sunset with no warning. 87 | } 88 | }, 89 | supportedEnvironments = new Dictionary>() {{"2020.1217.02", new List() {"production"}}} 90 | }, 91 | //updates = new LocatorResponse.Updates() 92 | }; 93 | 94 | var resp = JsonConvert.SerializeObject(response); 95 | return Content(resp, "application/json"); 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/MultiplayerController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Security.Claims; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Authorization; 9 | using Microsoft.AspNetCore.Http; 10 | using Newtonsoft.Json; 11 | using ProjectEarthServerAPI.Models.Buildplate; 12 | using ProjectEarthServerAPI.Models.Multiplayer; 13 | using ProjectEarthServerAPI.Util; 14 | 15 | namespace ProjectEarthServerAPI.Controllers 16 | { 17 | public class MultiplayerController : Controller 18 | { 19 | #region Buildplates 20 | 21 | [Authorize] 22 | [ApiVersion("1.1")] 23 | [Route("1/api/v{version:apiVersion}/multiplayer/buildplate/{buildplateId}/instances")] 24 | public async Task PostCreateInstance(string buildplateId) 25 | { 26 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 27 | var stream = new StreamReader(Request.Body); 28 | var body = await stream.ReadToEndAsync(); 29 | var parsedRequest = JsonConvert.DeserializeObject(body); 30 | 31 | var response = await MultiplayerUtils.CreateBuildplateInstance(authtoken, buildplateId, parsedRequest.playerCoordinate); 32 | return Content(JsonConvert.SerializeObject(response), "application/json"); 33 | } 34 | 35 | [Authorize] 36 | [ApiVersion("1.1")] 37 | [Route("1/api/v{version:apiVersion}/buildplates")] 38 | public IActionResult GetBuildplates() 39 | { 40 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 41 | 42 | var response = BuildplateUtils.GetBuildplatesList(authtoken); 43 | return Content(JsonConvert.SerializeObject(response), "application/json"); 44 | } 45 | 46 | #endregion 47 | 48 | [Authorize] 49 | [ApiVersion("1.1")] 50 | [Route("1/api/v{version:apiVersion}/multiplayer/partitions/{worldId}/instances/{instanceId}")] 51 | public IActionResult GetInstanceStatus(string worldId, Guid instanceId) 52 | { 53 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 54 | 55 | var response = MultiplayerUtils.CheckInstanceStatus(authtoken, instanceId); 56 | if (response == null) 57 | { 58 | return StatusCode(204); 59 | } 60 | else 61 | { 62 | return Content(JsonConvert.SerializeObject(response), "application/json"); 63 | } 64 | } 65 | 66 | [ApiVersion("1.1")] 67 | [Route("1/api/v{version:apiVersion}/private/server/command")] 68 | public async Task PostServerCommand() 69 | { 70 | var stream = new StreamReader(Request.Body); 71 | var body = await stream.ReadToEndAsync(); 72 | var parsedRequest = JsonConvert.DeserializeObject(body); 73 | 74 | var response = MultiplayerUtils.ExecuteServerCommand(parsedRequest); 75 | 76 | if (response == "ok") return Ok(); 77 | else return Content(response, "application/json"); 78 | } 79 | 80 | [ApiVersion("1.1")] 81 | [Route("1/api/v{version:apiVersion}/private/server/ws")] 82 | public async Task GetWebSocketServer() 83 | { 84 | if (HttpContext.WebSockets.IsWebSocketRequest) 85 | { 86 | var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); 87 | await MultiplayerUtils.AuthenticateServer(webSocket); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/PlayerController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Threading.Tasks; 4 | using System.IO; 5 | using Newtonsoft.Json; 6 | using ProjectEarthServerAPI.Models; 7 | using ProjectEarthServerAPI.Models.Player; 8 | using ProjectEarthServerAPI.Util; 9 | using Microsoft.AspNetCore.Authorization; 10 | using System.Security.Claims; 11 | using Serilog; 12 | using Uma.Uuid; 13 | 14 | namespace ProjectEarthServerAPI.Controllers 15 | { 16 | [Authorize] 17 | public class PlayerTokenController : Controller 18 | { 19 | [ApiVersion("1.1")] 20 | [ResponseCache(Duration = 11200)] 21 | [Route("1/api/v{version:apiVersion}/player/tokens")] 22 | public IActionResult Get() 23 | { 24 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 25 | var returnTokens = TokenUtils.GetSerializedTokenResponse(authtoken); 26 | 27 | Log.Debug($"[{authtoken}]: Requested tokens."); // Debug since this is spammed a lot 28 | 29 | return Content(returnTokens, "application/json"); 30 | } 31 | 32 | [ApiVersion("1.1")] 33 | [Route("1/api/v{version:apiVersion}/player/tokens/{token}/redeem")] // TODO: Proper testing 34 | public IActionResult RedeemToken(Uuid token) 35 | { 36 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 37 | 38 | var redeemedToken = TokenUtils.RedeemToken(authtoken, token); 39 | 40 | return Content(JsonConvert.SerializeObject(redeemedToken), "application/json"); 41 | } 42 | } 43 | 44 | [Authorize] 45 | [ApiVersion("1.1")] 46 | [ResponseCache(Duration = 11200)] 47 | [Route("1/api/v{version:apiVersion}/player/rubies")] 48 | public class PlayerRubiesController : Controller 49 | { 50 | public IActionResult Get() 51 | { 52 | var obj = RubyUtils.GetNormalRubyResponse(User.FindFirstValue(ClaimTypes.NameIdentifier)); 53 | var response = JsonConvert.SerializeObject(obj); 54 | return Content(response, "application/json"); 55 | } 56 | } 57 | 58 | [Authorize] 59 | [ApiVersion("1.1")] 60 | [ResponseCache(Duration = 11200)] 61 | [Route("1/api/v{version:apiVersion}/player/splitRubies")] 62 | public class PlayerSplitRubiesController : Controller 63 | { 64 | public IActionResult Get() 65 | { 66 | var obj = RubyUtils.ReadRubies(User.FindFirstValue(ClaimTypes.NameIdentifier)); 67 | var response = JsonConvert.SerializeObject(obj); 68 | return Content(response, "application/json"); 69 | } 70 | } 71 | 72 | [Authorize] 73 | public class PlayerChallengesController : Controller 74 | { 75 | [ApiVersion("1.1")] 76 | [Route("1/api/v{version:apiVersion}/player/challenges")] 77 | public IActionResult GetChallenges() 78 | { 79 | var authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 80 | var challenges = ChallengeUtils.ReloadChallenges(authtoken); 81 | return Content(JsonConvert.SerializeObject(challenges), "application/json"); 82 | } 83 | 84 | [ApiVersion("1.1")] 85 | [Route("1/api/v{version:apiVersion}/challenges/season/active/{challengeId}")] 86 | public IActionResult PutActivateChallenge(Guid challengeId) 87 | { 88 | var authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 89 | var success = ChallengeUtils.ActivateChallengeForPlayer(authtoken, challengeId); 90 | 91 | if (success) return Ok(); 92 | else return Unauthorized(); 93 | } 94 | } 95 | 96 | 97 | [Authorize] 98 | [ApiVersion("1.1")] 99 | [Route("1/api/v{version:apiVersion}/player/utilityBlocks")] 100 | public class PlayerUtilityBlocksController : Controller 101 | { 102 | public IActionResult Get() 103 | { 104 | var authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 105 | var response = UtilityBlockUtils.ReadUtilityBlocks(authtoken); 106 | return Content(JsonConvert.SerializeObject(response), "application/json"); 107 | //return Content("{\"result\":{\"crafting\":{\"1\":{\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Empty\",\"boostState\":null,\"unlockPrice\":null,\"streamVersion\":4},\"2\":{\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Locked\",\"boostState\":null,\"unlockPrice\":{\"cost\":1,\"discount\":0},\"streamVersion\":4},\"3\":{\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Locked\",\"boostState\":null,\"unlockPrice\":{\"cost\":1,\"discount\":0},\"streamVersion\":4}},\"smelting\":{\"1\":{\"fuel\":null,\"burning\":null,\"hasSufficientFuel\":null,\"heatAppliedToCurrentItem\":null,\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Empty\",\"boostState\":null,\"unlockPrice\":null,\"streamVersion\":4},\"2\":{\"fuel\":null,\"burning\":null,\"hasSufficientFuel\":null,\"heatAppliedToCurrentItem\":null,\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Locked\",\"boostState\":null,\"unlockPrice\":{\"cost\":1,\"discount\":0},\"streamVersion\":4},\"3\":{\"fuel\":null,\"burning\":null,\"hasSufficientFuel\":null,\"heatAppliedToCurrentItem\":null,\"sessionId\":null,\"recipeId\":null,\"output\":null,\"escrow\":[],\"completed\":0,\"available\":0,\"total\":0,\"nextCompletionUtc\":null,\"totalCompletionUtc\":null,\"state\":\"Locked\",\"boostState\":null,\"unlockPrice\":{\"cost\":1,\"discount\":0},\"streamVersion\":4}}},\"expiration\":null,\"continuationToken\":null,\"updates\":{}}", "application/json"); 108 | } 109 | } 110 | // 111 | // [Authorize] 112 | // [ApiVersion("1.1")] 113 | // [Route("1/api/v{version:apiVersion}/player/profile/{profileId}")] 114 | // public class PlayerProfileController : Controller 115 | // { 116 | // public IActionResult Get(string profileId) 117 | // { 118 | // var response = new ProfileResponse(ProfileUtils.ReadProfile(profileId)); 119 | // return Content(JsonConvert.SerializeObject(response), "application/json"); 120 | // } 121 | // } 122 | 123 | 124 | [Authorize] 125 | public class PlayerInventoryController : Controller 126 | { 127 | [ApiVersion("1.1")] 128 | [Route("1/api/v{version:apiVersion}/inventory/catalogv3")] 129 | public IActionResult GetCatalog() 130 | { 131 | return Content(JsonConvert.SerializeObject(StateSingleton.Instance.catalog), "application/json"); 132 | } 133 | 134 | [ApiVersion("1.1")] 135 | [Route("1/api/v{version:apiVersion}/inventory/survival")] 136 | public IActionResult GetSurvivalInventory() 137 | { 138 | var inv = InventoryUtils.ReadInventory(User.FindFirstValue(ClaimTypes.NameIdentifier)); 139 | var jsonstring = JsonConvert.SerializeObject(inv); 140 | return Content(jsonstring, "application/json"); 141 | } 142 | 143 | [ApiVersion("1.1")] 144 | [Route("1/api/v{version:apiVersion}/inventory/survival/hotbar")] 145 | public async Task PutItemInHotbar() 146 | { 147 | var stream = new StreamReader(Request.Body); 148 | var body = await stream.ReadToEndAsync(); 149 | 150 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 151 | var newHotbar = JsonConvert.DeserializeObject(body); 152 | var returnHotbar = InventoryUtils.EditHotbar(authtoken, newHotbar); 153 | 154 | return Content(JsonConvert.SerializeObject(returnHotbar.Item2)); 155 | } 156 | } 157 | 158 | [Authorize] 159 | [ApiVersion("1.1")] 160 | [Route("1/api/v{version:apiVersion}/settings")] 161 | public class PlayerSettingsController : Controller 162 | { 163 | public IActionResult Get() 164 | { 165 | return Content("{\"result\":{\"encounterinteractionradius\":40.0,\"tappableinteractionradius\":70.0,\"tappablevisibleradius\":-5.0,\"targetpossibletappables\":100.0,\"tile0\":10537.0,\"slowrequesttimeout\":2500.0,\"cullingradius\":50.0,\"commontapcount\":3.0,\"epictapcount\":7.0,\"speedwarningcooldown\":3600.0,\"mintappablesrequiredpertile\":22.0,\"targetactivetappables\":30.0,\"tappablecullingradius\":500.0,\"raretapcount\":5.0,\"requestwarningtimeout\":10000.0,\"speedwarningthreshold\":11.176,\"asaanchormaxplaneheightthreshold\":0.5,\"maxannouncementscount\":0.0,\"removethislater\":23.0,\"crystalslotcap\":3.0,\"crystaluncommonduration\":10.0,\"crystalrareduration\":10.0,\"crystalepicduration\":10.0,\"crystalcommonduration\":10.0,\"crystallegendaryduration\":10.0,\"maximumpersonaltimedchallenges\":3.0,\"maximumpersonalcontinuouschallenges\":3.0},\"expiration\":null,\"continuationToken\":null,\"updates\":{}}", "application/json"); 166 | } 167 | } // TODO: Fixed String 168 | 169 | [Authorize] 170 | [ApiVersion("1.1")] 171 | [Route("1/api/v{version:apiVersion}/recipes")] 172 | public class PlayerRecipeController : Controller 173 | { 174 | public IActionResult Get() 175 | { 176 | var recipeString = System.IO.File.ReadAllText(StateSingleton.Instance.config.recipesFileLocation); // Since the serialized version has the properties mixed up 177 | return Content(recipeString, "application/json"); 178 | } 179 | } 180 | 181 | [Authorize] 182 | [ApiVersion("1.1")] 183 | public class JournalController : Controller 184 | { 185 | [Route("1/api/v{version:apiVersion}/journal/catalog")] 186 | public IActionResult GetCatalog() 187 | { 188 | var fs = new StreamReader(StateSingleton.Instance.config.journalCatalogFileLocation); 189 | return Content(fs.ReadToEnd(), "application/json"); 190 | } 191 | 192 | [Route("1/api/v{version:apiVersion}/player/journal")] 193 | public IActionResult GetJournal() 194 | { 195 | var authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 196 | var resp = JournalUtils.ReadJournalForPlayer(authtoken); 197 | 198 | return Content(JsonConvert.SerializeObject(resp), "application/json"); 199 | } 200 | } 201 | 202 | [Authorize] 203 | public class PlayerBoostController : Controller 204 | { 205 | [ApiVersion("1.1")] 206 | [Route("1/api/v{version:apiVersion}/boosts")] 207 | public IActionResult Get() 208 | { 209 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 210 | var responseobj = BoostUtils.UpdateBoosts(authtoken); 211 | var response = JsonConvert.SerializeObject(responseobj); 212 | return Content(response, "application/json"); 213 | } 214 | 215 | [ApiVersion("1.1")] 216 | [Route("1/api/v{version:apiVersion}/boosts/potions/{boostId}/activate")] 217 | public IActionResult GetRedeemBoost(Guid boostId) 218 | { 219 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 220 | var returnUpdates = BoostUtils.ActivateBoost(authtoken, boostId); 221 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 222 | } 223 | 224 | [ApiVersion("1.1")] 225 | [Route("1/api/v{version:apiVersion}/boosts/{boostInstanceId}")] 226 | public IActionResult DeleteBoost(string boostInstanceId) 227 | { 228 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 229 | var returnUpdates = BoostUtils.RemoveBoost(authtoken, boostInstanceId); 230 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 231 | } 232 | } // TODO: In Progress 233 | 234 | [Authorize] 235 | [ApiVersion("1.1")] 236 | [Route("1/api/v{version:apiVersion}/features")] 237 | public class PlayerFeaturesController : Controller 238 | { 239 | public IActionResult Get() 240 | { 241 | var responseobj = new FeaturesResponse() { result = new FeaturesResult(), updates = new Updates() }; 242 | var response = JsonConvert.SerializeObject(responseobj); 243 | return Content(response, "application/json"); 244 | } 245 | } // TODO: Fixed String, just get from a JSON for serverwide settings 246 | 247 | [Authorize] 248 | public class PlayerShopController : Controller 249 | { 250 | [ApiVersion("1.1")] 251 | [Route("1/api/v{version:apiVersion}/products/catalog")] 252 | public IActionResult GetProductCatalog() 253 | { 254 | var catalog = System.IO.File.ReadAllText(StateSingleton.Instance.config.productCatalogFileLocation); // Since the serialized version has the properties mixed up 255 | return Content(catalog, "application/json"); 256 | } 257 | } // TODO: Needs Playfab counterpart. When that is in place we can implement buildplate previews. 258 | } 259 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/ResourcePackController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | using System.IO; 7 | using Newtonsoft.Json; 8 | using ProjectEarthServerAPI.Models; 9 | using Serilog; 10 | 11 | namespace ProjectEarthServerAPI.Controllers 12 | { 13 | //Wheres the resource pack? 14 | 15 | [ApiVersion("1.1")] 16 | [Route("api/v{version:apiVersion}/resourcepacks/2020.1217.02/default")] 17 | public class ResourcePackController : ControllerBase 18 | { 19 | [HttpGet] 20 | public ContentResult Get() 21 | { 22 | ResourcePackResponse response = new ResourcePackResponse 23 | { 24 | result = new List() 25 | { 26 | new ResourcePackResponse.Result 27 | { 28 | order = 0, 29 | parsedResourcePackVersion = new List() {2020, 1214, 4}, 30 | relativePath = "availableresourcepack/resourcepacks/dba38e59-091a-4826-b76a-a08d7de5a9e2-1301b0c257a311678123b9e7325d0d6c61db3c35", //Naming the endpoint the same thing for consistency. this might not be needed. 31 | resourcePackVersion = "2020.1214.04", 32 | resourcePackId = "dba38e59-091a-4826-b76a-a08d7de5a9e2" 33 | } 34 | }, 35 | updates = new Updates(), 36 | continuationToken = null, 37 | expiration = null 38 | }; 39 | return Content(JsonConvert.SerializeObject(response), "application/json"); 40 | } 41 | } 42 | 43 | //Heres the resource pack! 44 | [Route("cdn/availableresourcepack/resourcepacks/dba38e59-091a-4826-b76a-a08d7de5a9e2-1301b0c257a311678123b9e7325d0d6c61db3c35")] 45 | public class ResourcePackCdnController : ControllerBase 46 | { 47 | public IActionResult Get() 48 | { 49 | String resourcePackFilePath = @"./data/resourcepacks/vanilla.zip"; //resource packs are distributed as renamed zip files containing an MCpack 50 | 51 | if (!System.IO.File.Exists(resourcePackFilePath)) 52 | { 53 | Log.Error("[Resourcepacks] Error! Resource pack file not found."); 54 | return BadRequest(); //we cannot serve you. 55 | } 56 | 57 | byte[] fileData = System.IO.File.ReadAllBytes(resourcePackFilePath); //Namespaces 58 | var cd = new System.Net.Mime.ContentDisposition {FileName = "dba38e59-091a-4826-b76a-a08d7de5a9e2-1301b0c257a311678123b9e7325d0d6c61db3c35", Inline = true}; 59 | Response.Headers.Add("Content-Disposition", cd.ToString()); 60 | 61 | 62 | return File(fileData, "application/octet-stream", "dba38e59-091a-4826-b76a-a08d7de5a9e2-1301b0c257a311678123b9e7325d0d6c61db3c35"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/SigninController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Authorization; 9 | using Newtonsoft.Json; 10 | using ProjectEarthServerAPI.Models; 11 | using ProjectEarthServerAPI.Util; 12 | using Serilog; 13 | 14 | namespace ProjectEarthServerAPI.Controllers 15 | { 16 | [ApiVersion("1.1")] 17 | [Route("api/v{version:apiVersion}/player/profile/{profileID}")] 18 | public class SigninController : ControllerBase 19 | { 20 | [Authorize] 21 | [HttpGet] 22 | public async Task Get(string profileID) 23 | { 24 | var response = new ProfileResponse(ProfileUtils.ReadProfile(profileID)); 25 | return Content(JsonConvert.SerializeObject(response), "application/json"); 26 | } 27 | 28 | 29 | [HttpPost] 30 | public async Task Post(string jsonstring, string profileID) 31 | { 32 | if (profileID != "signin") 33 | { 34 | ContentResult badReq = new ContentResult(); 35 | badReq.StatusCode = 400; 36 | return badReq; 37 | } 38 | 39 | var httprequest = Request.Body; 40 | StreamReader sr = new StreamReader(httprequest); 41 | var request = JsonConvert.DeserializeObject(await sr.ReadToEndAsync()); 42 | 43 | var playerid = request.sessionTicket.Split("-")[0]; 44 | 45 | var response = new SigninResponse.ResponseTemplate() 46 | { 47 | result = new SigninResponse.Result() 48 | { 49 | AuthenticationToken = playerid, 50 | BasePath = "/1", 51 | Tokens = TokenUtils.GetSigninTokens(playerid), 52 | ClientProperties = new object(), 53 | Updates = new Updates() 54 | }, 55 | updates = new Updates() 56 | }; 57 | 58 | var resp = JsonConvert.SerializeObject(response, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); 59 | 60 | Log.Information($"[{playerid}]: Logged in."); 61 | 62 | return Content(resp, "application/json"); 63 | //return Content( 64 | // "{\"result\":{\"authenticationToken\":\"" + playerid + "\",\"tokens\":{\"9b0acea5-8eaf-4ccc-8f8d-187784dc5544\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"605dc84c-f6d1-442b-8901-69b7b89a0cc8\"}},\"c025a117-de76-49c8-924a-9c56986d7a98\":{\"lifetime\":\"Persistent\",\"clientType\":\"ftue.context.pickup_mobs\",\"clientProperties\":{}},\"89e11f11-2582-47da-968d-764db91c7bd7\":{\"lifetime\":\"Persistent\",\"clientType\":\"ftue.context.build_with_friends.intro\",\"clientProperties\":{}},\"fc3028b9-36bf-473f-87ab-598ddc3d468f\":{\"lifetime\":\"Persistent\",\"clientType\":\"ftue.context.build_with_friends.menu\",\"clientProperties\":{}},\"80c106d4-3f80-4f42-8f1c-211278b1c2ef\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"9a2bd5aa-015d-419b-9c3a-d80dde87e312\"}},\"21dabe32-6d4e-4954-ab0d-8a21f5e51665\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.notification\",\"clientProperties\":{\"seasonId\":\"9a2bd5aa-015d-419b-9c3a-d80dde87e312\"}},\"7b6ecfe4-ad61-4353-af7b-cd005f0e3d1e\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"2ba1bbef-fc22-42e5-b1af-81f00c130a5b\"}},\"e74024fd-6c40-42b6-ba84-1a315e1f5982\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"cc456b52-1586-4e75-b7e9-aa811f609567\"}},\"a33fd95f-f567-4c53-ae6e-4fad94975e80\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"2bf15fa5-50ed-41ce-a2c4-b870028ed991\"}},\"b730a241-3966-4731-96c4-25387493f9d2\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"98749e7b-6a14-4fac-a851-3a68511c3f77\"}},\"e23a3f79-6362-4fff-8d10-636ab4052d10\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"07f1e716-b1d5-49df-ac81-fb8ce184746e\"}},\"4fbcb172-c215-482e-870b-6d3288a42cfb\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"ba777233-973b-4536-96e9-3e8abb3ae10a\"}},\"d702f10e-d616-444a-bf4b-3e2b9cbe3d07\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"5686ddbd-3da4-46da-a719-8e95e486cdb4\"}},\"9e9addd7-812e-4135-8edc-1155bf712240\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{}},\"39a32e0f-f19d-442a-9ae3-26785dd4b14a\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"3064a2c5-e5f6-44ed-9561-7e972e1c1410\"}},\"54b85e5d-1485-432f-929e-3845edb1e301\":{\"lifetime\":\"Persistent\",\"clientType\":\"empty.bucket.returned\",\"clientProperties\":{}},\"ab21c03d-18c0-441a-b78a-79fa429391d8\":{\"lifetime\":\"Persistent\",\"clientType\":\"ftue.context.adventures\",\"clientProperties\":{},\"rewards\":{\"inventory\":[],\"buildplates\":[],\"challenges\":[],\"personaItems\":[],\"utilityBlocks\":[]}},\"98c38d85-525e-4edf-849a-58ff68219c37\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"fe8a85b4-7b73-4182-8e46-adb894811f32\"}},\"3a99af70-bbf3-4041-8094-491bed0c3845\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.notification\",\"clientProperties\":{\"seasonId\":\"fe8a85b4-7b73-4182-8e46-adb894811f32\"}},\"af694f0c-b448-44a2-9d73-b2cc5e74b271\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"4c5bd76e-d57a-4ede-9193-eff0c12a6f8b\"}},\"878e2acc-238f-4bd3-870e-ebba2d374c68\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.welcome\",\"clientProperties\":{\"seasonId\":\"3604cd1b-dc4b-4e69-bf8f-8774c81caa74\"}},\"d5f19783-57ee-4bf9-883b-736680e9237f\":{\"lifetime\":\"Persistent\",\"clientType\":\"seasons.notification\",\"clientProperties\":{\"seasonId\":\"3604cd1b-dc4b-4e69-bf8f-8774c81caa74\"}}},\"updates\":{},\"basePath\":\"/1\",\"mrToken\":null,\"mixedReality\":null,\"streams\":null,\"clientProperties\":{}},\"expiration\":null,\"continuationToken\":null,\"updates\":{}}", 65 | // "application/json"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/SmeltingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Threading.Tasks; 6 | using Newtonsoft.Json; 7 | using ProjectEarthServerAPI.Models.Features; 8 | using ProjectEarthServerAPI.Util; 9 | using Microsoft.AspNetCore.Authorization; 10 | using System.Security.Claims; 11 | using ProjectEarthServerAPI.Models; 12 | using Serilog; 13 | 14 | namespace ProjectEarthServerAPI.Controllers 15 | { 16 | // TODO: Not done. Rewards need inventory implementation, timers for smelting process, and recipeId -> recipe time checks 17 | [Authorize] 18 | public class SmeltingController : Controller 19 | { 20 | [ApiVersion("1.1")] 21 | [Route("1/api/v{version:apiVersion}/smelting/{slot}/start")] 22 | public async Task PostNewSmeltingJob(int slot) 23 | { 24 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 25 | 26 | var stream = new StreamReader(Request.Body); 27 | var body = await stream.ReadToEndAsync(); 28 | 29 | var req = JsonConvert.DeserializeObject(body); 30 | 31 | var smeltingJob = SmeltingUtils.StartSmeltingJob(authtoken, slot, req); 32 | 33 | var updateResponse = new UpdateResponse {updates = new Updates()}; 34 | 35 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 36 | 37 | updateResponse.updates.smelting = nextStreamId; 38 | updateResponse.updates.inventory = nextStreamId; 39 | 40 | return Content(JsonConvert.SerializeObject(updateResponse), "application/json"); 41 | //return Accepted(Content(returnUpdates, "application/json")); 42 | } 43 | 44 | [ApiVersion("1.1")] 45 | [Route("1/api/v{version:apiVersion}/smelting/finish/price")] 46 | public IActionResult GetSmeltingPrice(int slot) 47 | { 48 | TimeSpan remainingTime = TimeSpan.Parse(Request.Query["remainingTime"]); 49 | var returnPrice = new CraftingPriceResponse {result = new CraftingPrice {cost = 1, discount = 0, validTime = remainingTime}, updates = new Updates()}; 50 | 51 | return Content(JsonConvert.SerializeObject(returnPrice), "application/json"); 52 | } 53 | 54 | 55 | [ApiVersion("1.1")] 56 | [Route("1/api/v{version:apiVersion}/smelting/{slot}")] 57 | public IActionResult GetSmeltingStatus(int slot) 58 | { 59 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 60 | 61 | var smeltingStatus = SmeltingUtils.GetSmeltingJobInfo(authtoken, slot); 62 | 63 | return Content(JsonConvert.SerializeObject(smeltingStatus), "application/json"); 64 | //return Accepted(Content(returnTokens, "application/json")); 65 | } 66 | 67 | [ApiVersion("1.1")] 68 | [Route("1/api/v{version:apiVersion}/smelting/{slot}/collectItems")] 69 | public IActionResult GetCollectSmeltingItems(int slot) 70 | { 71 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 72 | 73 | var returnUpdates = SmeltingUtils.FinishSmeltingJob(authtoken, slot); 74 | 75 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 76 | //return Accepted(Content(returnTokens, "application/json")); 77 | } 78 | 79 | [ApiVersion("1.1")] 80 | [Route("1/api/v{version:apiVersion}/smelting/{slot}/stop")] 81 | public IActionResult GetStopSmeltingJob(int slot) 82 | { 83 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 84 | 85 | var returnUpdates = SmeltingUtils.CancelSmeltingJob(authtoken, slot); 86 | 87 | return Accepted(); 88 | 89 | //return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 90 | //return Accepted(Content(returnTokens, "application/json")); 91 | } 92 | 93 | [ApiVersion("1.1")] 94 | [Route("1/api/v{version:apiVersion}/smelting/{slot}/unlock")] 95 | public IActionResult GetUnlockSmeltingSlot(int slot) 96 | { 97 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 98 | 99 | var returnUpdates = SmeltingUtils.UnlockSmeltingSlot(authtoken, slot); 100 | 101 | return Content(JsonConvert.SerializeObject(returnUpdates), "application/json"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Controllers/TappablesController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using Newtonsoft.Json; 4 | using ProjectEarthServerAPI.Models.Features; 5 | using ProjectEarthServerAPI.Models.Player; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Security.Claims; 11 | using System.Threading.Tasks; 12 | using ProjectEarthServerAPI.Models; 13 | using ProjectEarthServerAPI.Util; 14 | using Serilog; 15 | 16 | namespace ProjectEarthServerAPI.Controllers 17 | { 18 | [Authorize] 19 | public class TappablesController : Controller 20 | { 21 | [HttpPost] 22 | [ApiVersion("1.1")] 23 | [Route("1/api/v{version:apiVersion}/tappables/{x}_{y}")] 24 | public async Task Post(int x, int y) 25 | { 26 | string authtoken = User.FindFirstValue(ClaimTypes.NameIdentifier); 27 | var stream = new StreamReader(Request.Body); 28 | var body = await stream.ReadToEndAsync(); 29 | 30 | 31 | var req = JsonConvert.DeserializeObject(body); 32 | 33 | 34 | Random random = new Random(); 35 | 36 | var type = StateSingleton.Instance.activeTappableTypes[req.id]; 37 | List> availableDropSets = null; 38 | try 39 | { 40 | availableDropSets = StateSingleton.Instance.tappableData[type]; 41 | } 42 | catch (Exception e) 43 | { 44 | Log.Error("[Tappables] no json file for tappable type" + type + " exists in data/tappables. Using backup of dirt (f0617d6a-c35a-5177-fcf2-95f67d79196d)"); 45 | availableDropSets = new List>(); 46 | availableDropSets.Add(new List() 47 | { 48 | "f0617d6a-c35a-5177-fcf2-95f67d79196d" 49 | }); //dirt for you... sorry :/ 50 | } 51 | 52 | if (availableDropSets == null) 53 | { 54 | Log.Error($"[Tappables] No drop sets for {type}!"); 55 | } 56 | 57 | var targetDropSet = availableDropSets[random.Next(0, availableDropSets.Count)]; 58 | if (targetDropSet == null) 59 | { 60 | Log.Error($"[Tappables] targetDropSet is null! Available drop set count was {availableDropSets.Count}"); 61 | } 62 | 63 | var rewards = new RewardComponent[targetDropSet.Count]; 64 | for (int i = 0; i < targetDropSet.Count; i++) 65 | { 66 | rewards[i] = new RewardComponent() { Amount = random.Next(1, 3), Id = new Guid(targetDropSet[i]) }; 67 | } 68 | 69 | var response = new TappableResponse() 70 | { 71 | result = new TappableResponse.Result() 72 | { 73 | token = new Token() 74 | { 75 | clientProperties = new Dictionary(), 76 | clientType = "redeemtappable", 77 | lifetime = "Persistent", 78 | rewards = new Rewards() { ExperiencePoints = 400, Inventory = rewards } 79 | } 80 | } 81 | }; 82 | 83 | response.updates = RewardUtils.RedeemRewards(authtoken, response.result.token.rewards); 84 | response.result.updates = response.updates; 85 | 86 | return Content(JsonConvert.SerializeObject(response), "application/json"); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/CDN/ResourcePackResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ProjectEarthServerAPI.Models 7 | { 8 | public class ResourcePackResponse 9 | { 10 | public class Result 11 | { 12 | public int order { get; set; } 13 | public List parsedResourcePackVersion { get; set; } 14 | public string relativePath { get; set; } 15 | public string resourcePackId { get; set; } 16 | public string resourcePackVersion { get; set; } 17 | } 18 | 19 | public object continuationToken { get; set; } 20 | public object expiration { get; set; } 21 | public List result { get; set; } 22 | public Updates updates { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/CatalogResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.IO; 6 | using Newtonsoft.Json; 7 | 8 | namespace ProjectEarthServerAPI.Models.Features 9 | { 10 | /// 11 | /// Models an item catalog 12 | /// 13 | public class CatalogResponse 14 | { 15 | //===Model 16 | 17 | #region Model 18 | 19 | //Efficiency Classes 20 | public class EfficiencyMap 21 | { 22 | public double hand { get; set; } 23 | public double hoe { get; set; } 24 | public double axe { get; set; } 25 | public double shovel { get; set; } 26 | public double pickaxe_1 { get; set; } 27 | public double pickaxe_2 { get; set; } 28 | public double pickaxe_3 { get; set; } 29 | public double pickaxe_4 { get; set; } 30 | public double pickaxe_5 { get; set; } 31 | public double sword { get; set; } 32 | public double sheers { get; set; } 33 | } 34 | 35 | public class EfficiencyCategory 36 | { 37 | public EfficiencyMap efficiencyMap { get; set; } 38 | } 39 | 40 | public class Result 41 | { 42 | public Dictionary efficiencyCategories { get; set; } 43 | public List items { get; set; } 44 | } 45 | 46 | public Result result { get; set; } 47 | public object expiration { get; set; } 48 | public object continuationToken { get; set; } 49 | public Updates updates { get; set; } 50 | 51 | #endregion 52 | 53 | #region Functions 54 | 55 | //===Load and save 56 | public static CatalogResponse FromFiles(string itemsFolderLocation, string efficiencyCategoriesFolderLocation) 57 | { 58 | Dictionary efficiencyCategories = new Dictionary(); 59 | foreach (string efficiencyCategoryFile in Directory.GetFiles(efficiencyCategoriesFolderLocation)) 60 | { 61 | string efficiencyCategory = File.ReadAllText(efficiencyCategoryFile); 62 | EfficiencyCategory cat = new EfficiencyCategory() {efficiencyMap = JsonConvert.DeserializeObject(efficiencyCategory)}; 63 | efficiencyCategories.Add(Path.GetFileNameWithoutExtension(efficiencyCategoryFile), cat); 64 | } 65 | 66 | List items = new List(); 67 | foreach (string itemFile in Directory.GetFiles(itemsFolderLocation)) 68 | { 69 | string item = File.ReadAllText(itemFile); 70 | items.Add(JsonConvert.DeserializeObject(item)); 71 | } 72 | 73 | return new CatalogResponse() {result = new Result() {efficiencyCategories = efficiencyCategories, items = items}, updates = new Updates()}; 74 | } 75 | 76 | #endregion 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/CraftingRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace ProjectEarthServerAPI.Models.Features 4 | { 5 | public class CraftingRequest 6 | { 7 | [JsonProperty("ingredients")] 8 | public InputItem[] Ingredients { get; set; } 9 | 10 | [JsonProperty("multiplier")] 11 | public int Multiplier { get; set; } 12 | 13 | [JsonProperty("recipeId")] 14 | public string RecipeId { get; set; } 15 | 16 | [JsonProperty("sessionId")] 17 | public string SessionId { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/CraftingResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ProjectEarthServerAPI.Models.Player; 4 | 5 | namespace ProjectEarthServerAPI.Models.Features 6 | { 7 | public class CraftingUpdates 8 | { 9 | public Updates updates { get; set; } 10 | } 11 | 12 | public class CraftingPrice 13 | { 14 | public int cost { get; set; } 15 | public int discount { get; set; } 16 | public TimeSpan validTime { get; set; } 17 | } 18 | 19 | public class CraftingPriceResponse 20 | { 21 | public CraftingPrice result { get; set; } 22 | public Updates updates { get; set; } 23 | } 24 | 25 | public class CraftingSlotResponse 26 | { 27 | public CraftingSlotInfo result { get; set; } 28 | public Updates updates { get; set; } 29 | } 30 | 31 | public class CollectItemsResponse 32 | { 33 | public CollectItemsInfo result { get; set; } 34 | public Updates updates { get; set; } 35 | } 36 | 37 | public class CollectItemsInfo 38 | { 39 | public Rewards rewards { get; set; } 40 | } 41 | 42 | public class CraftingSlotInfo // crafting/slot, crafting/1, crafting/2 - Also used in utilityBlocks 43 | { 44 | public string sessionId { get; set; } // ID of Session for Job 45 | public string recipeId { get; set; } // ID of Recipe in use 46 | public RecipeOutput output { get; set; } // Output items of process 47 | public InputItem[] escrow { get; set; } // Input Items for process, empty when process is finished 48 | public int completed { get; set; } // Completed - maybe the same as available above, or just unused 49 | public int available { get; set; } // 0 - Currently not available or working, 1 - Available for collecting 50 | public int total { get; set; } // Total number of output items 51 | public DateTime? nextCompletionUtc { get; set; } // Time when process is complete 52 | public DateTime? totalCompletionUtc { get; set; } // Time of completion 53 | public string state { get; set; } // State: Active, Completed or Locked 54 | public BoostState boostState { get; set; } // See class below 55 | public UnlockPrice unlockPrice { get; set; } // Price to unlock item 56 | public ulong streamVersion { get; set; } // StreamVersion with changes, unused in our version 57 | } 58 | 59 | public class BoostState 60 | { 61 | // TODO: Use boost to find out how to structure this 62 | public int multiplier { get; set; } 63 | public InventoryResponse.ItemInstance boostItem { get; set; } // Hope 64 | } 65 | 66 | public class UnlockPrice 67 | { 68 | public int cost { get; set; } // Cost in rubies of unlocking 69 | public int discount { get; set; } // Discount to give 70 | } 71 | 72 | public class InputItem 73 | { 74 | public Guid itemId { get; set; } // Item ID 75 | public InventoryResponse.ItemInstance[] itemInstanceIds { get; set; } // Only used in unstackable items (tools, etc.) 76 | public int quantity { get; set; } // Quantity of item 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | 6 | namespace ProjectEarthServerAPI.Models.Features 7 | { 8 | public class Item 9 | { 10 | public Guid id { get; set; } 11 | public ItemInfo item { get; set; } 12 | 13 | [JsonConverter(typeof(StringEnumConverter))] 14 | public Rarity rarity { get; set; } 15 | 16 | public int fragmentsRequired { get; set; } 17 | public bool stacks { get; set; } 18 | public BurnRate burnRate { get; set; } 19 | public List fuelReturnItems { get; set; } 20 | public List consumeReturnItems { get; set; } 21 | public bool deprecated { get; set; } 22 | public int? experience { get; set; } 23 | public ExperiencePoints experiencePoints { get; set; } // This could be a Dictionary ? 24 | public string category { get; set; } 25 | 26 | public enum Rarity 27 | { 28 | Common, 29 | Uncommon, 30 | Rare, 31 | Epic, 32 | Legendary, 33 | OOBE 34 | } 35 | 36 | public class ItemInfo 37 | { 38 | public string name { get; set; } 39 | public int aux { get; set; } 40 | public string type { get; set; } 41 | public string useType { get; set; } 42 | public float? tapSpeed { get; set; } 43 | public float? heal { get; set; } 44 | public float? nutrition { get; set; } 45 | public float? mobDamage { get; set; } 46 | public float? blockDamage { get; set; } 47 | public float? health { get; set; } 48 | public BlockMetadata blockMetadata { get; set; } 49 | public ItemMetadata ItemMetadata { get; set; } 50 | public BoostMetadata? boostMetadata { get; set; } 51 | public JournalMetadata journalMetadata { get; set; } 52 | public AudioMetadata? audioMetadata { get; set; } 53 | public Dictionary clientProperties { get; set; } 54 | } 55 | 56 | public class BurnRate 57 | { 58 | public int heatPerSecond { get; set; } 59 | public int burnTime { get; set; } 60 | } 61 | 62 | public class BlockMetadata 63 | { 64 | public float? health { get; set; } 65 | public string efficiencyCategory { get; set; } 66 | } 67 | 68 | public class ItemMetadata 69 | { 70 | public string useType { get; set; } 71 | public string alternativeUseType { get; set; } 72 | public float? mobDamage { get; set; } 73 | public float? blockDamage { get; set; } 74 | public float? weakDamage { get; set; } 75 | public float? nutrition { get; set; } 76 | public float? heal { get; set; } 77 | public string? efficiencyType { get; set; } 78 | public float? maxHealth { get; set; } 79 | } 80 | 81 | public class BoostMetadata 82 | { 83 | public string name { get; set; } 84 | public string attribute { get; set; } 85 | public string type { get; set; } 86 | public bool canBeDeactivated { get; set; } 87 | public bool canBeRemoved { get; set; } 88 | public DateTime? activeDuration { get; set; } 89 | public bool additive { get; set; } 90 | public object level { get; set; } 91 | public List effects { get; set; } 92 | public string scenario { get; set; } 93 | public DateTime? cooldown { get; set; } 94 | } 95 | 96 | public class JournalMetadata 97 | { 98 | public string groupKey { get; set; } 99 | public int experience { get; set; } 100 | public int order { get; set; } 101 | public string behavior { get; set; } 102 | public string biome { get; set; } 103 | } 104 | 105 | public class Sounds 106 | { 107 | public string journal { get; set; } 108 | } 109 | 110 | public class AudioMetadata 111 | { 112 | public Sounds sounds { get; set; } 113 | public string defaultSound { get; set; } 114 | } 115 | 116 | public class ExperiencePoints 117 | { 118 | public int tappable { get; set; } 119 | public int? encounter { get; set; } 120 | } 121 | 122 | public class Effect 123 | { 124 | public string type { get; set; } 125 | public TimeSpan? duration { get; set; } 126 | public double? value { get; set; } 127 | public string unit { get; set; } 128 | public string targets { get; set; } 129 | public List items { get; set; } // Which items it affects 130 | public List itemScenarios { get; set; } // Where this effect is used 131 | public string activation { get; set; } // Triggered - on a trigger, Instant - directly, for food, Timed - Has a timer for completion 132 | public object modifiesType { get; set; } // Only health or null 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/JournalResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | using ProjectEarthServerAPI.Models.Player; 6 | using ProjectEarthServerAPI.Util; 7 | 8 | namespace ProjectEarthServerAPI.Models.Features 9 | { 10 | public class JournalResponse 11 | { 12 | public PlayerJournal result { get; set; } 13 | public Updates updates { get; set; } 14 | 15 | public JournalResponse() 16 | { 17 | result = new PlayerJournal {activityLog = new List(), inventoryJournal = new Dictionary(),}; 18 | } 19 | } 20 | 21 | public class PlayerJournal 22 | { 23 | public List activityLog { get; set; } 24 | public Dictionary inventoryJournal { get; set; } 25 | } 26 | 27 | public class Activity 28 | { 29 | [JsonConverter(typeof(DateTimeConverter))] 30 | public DateTime eventTime { get; set; } 31 | 32 | public ActivityProperties properties { get; set; } 33 | public Rewards rewards { get; set; } 34 | 35 | [JsonConverter(typeof(StringEnumConverter))] 36 | public Scenario scenario { get; set; } 37 | } 38 | 39 | public class ActivityProperties 40 | { 41 | [JsonConverter(typeof(StringEnumConverter))] 42 | public ChallengeDuration duration { get; set; } 43 | 44 | public uint order { get; set; } 45 | public Guid referenceId { get; set; } 46 | } 47 | 48 | public class JournalEntry 49 | { 50 | public uint amountCollected { get; set; } 51 | 52 | [JsonConverter(typeof(DateTimeConverter))] 53 | public DateTime firstSeen { get; set; } 54 | 55 | [JsonConverter(typeof(DateTimeConverter))] 56 | public DateTime lastSeen { get; set; } 57 | } 58 | 59 | public enum Scenario 60 | { 61 | AdventurePlayed, 62 | BuildplatePlayed, 63 | ChallengeCompleted, 64 | CraftingJobCompleted, 65 | JournalContentCollected, 66 | SmeltingJobCompleted, 67 | TappableCollected 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/ProductCatalogResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Newtonsoft.Json; 4 | 5 | namespace ProjectEarthServerAPI.Models.Features 6 | { 7 | public class ProductCatalogResponse 8 | { 9 | public List result { get; set; } 10 | public Updates updates { get; set; } 11 | 12 | public static ProductCatalogResponse FromFile(string path) 13 | { 14 | var jsontext = File.ReadAllText(path); 15 | return JsonConvert.DeserializeObject(jsontext); 16 | } 17 | } 18 | 19 | public class ProductCatalogItem 20 | { 21 | public int id { get; set; } 22 | public Item.BoostMetadata boostMetadata { get; set; } 23 | public string name { get; set; } 24 | public bool deprecated { get; set; } 25 | public string toolsVersion { get; set; } // Possibly date? One is 200228.204513 26 | public string type { get; set; } // Either MiniFig or NfcMiniFig 27 | public Rewards rewards { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/Recipes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | 6 | namespace ProjectEarthServerAPI.Models.Features 7 | { 8 | public class Recipes 9 | { 10 | public Result result { get; set; } 11 | 12 | public static Recipes FromFile(string filepath) 13 | { 14 | var recipetext = File.ReadAllText(filepath); 15 | return JsonConvert.DeserializeObject(recipetext); 16 | } 17 | } 18 | 19 | public class Result 20 | { 21 | public List crafting { get; set; } 22 | public List smelting { get; set; } 23 | } 24 | 25 | public class Recipe 26 | { 27 | public string category { get; set; } 28 | public RecipeIngredients[] ingredients { get; set; } 29 | public DateTime duration { get; set; } // HH:MM:SS 30 | public string id { get; set; } 31 | public RecipeOutput output { get; set; } 32 | public bool deprecated { get; set; } 33 | public ReturnItem[] returnItems { get; set; } 34 | } 35 | 36 | public class SmeltingRecipe 37 | { 38 | public string inputItemId { get; set; } 39 | public int heatRequired { get; set; } 40 | public string id { get; set; } 41 | public RecipeOutput output { get; set; } 42 | public bool deprecated { get; set; } 43 | public ReturnItem[] returnItems { get; set; } 44 | } 45 | 46 | public class RecipeIngredients 47 | { 48 | public int quantity { get; set; } 49 | public Guid[] items { get; set; } 50 | } 51 | 52 | public class RecipeOutput 53 | { 54 | public Guid itemId { get; set; } 55 | public int quantity { get; set; } 56 | } 57 | 58 | public class ReturnItem 59 | { 60 | public Guid id { get; set; } 61 | public int amount { get; set; } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/SmeltingRequest.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace ProjectEarthServerAPI.Models.Features 4 | { 5 | public class SmeltingRequest 6 | { 7 | [JsonProperty("input")] 8 | public InputItem Ingredient { get; set; } 9 | 10 | [JsonProperty("fuel")] 11 | public InputItem FuelIngredient { get; set; } 12 | 13 | [JsonProperty("multiplier")] 14 | public int Multiplier { get; set; } 15 | 16 | [JsonProperty("recipeId")] 17 | public string RecipeId { get; set; } 18 | 19 | [JsonProperty("sessionId")] 20 | public string SessionId { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/SmeltingResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using ProjectEarthServerAPI.Models.Player; 4 | 5 | namespace ProjectEarthServerAPI.Models.Features 6 | { 7 | public class SmeltingSlotInfo // smelting endpoints, like crafting, also used in utilityBlocks 8 | { 9 | public FuelInfo fuel { get; set; } // Why is this used? When used, same info as burning.fuel, but quantity always 0? 10 | public BurningItems burning { get; set; } // Information about currently burning items/burn rate 11 | public bool? hasSufficientFuel { get; set; } // If item has enough fuel to complete process. 12 | public float? heatAppliedToCurrentItem { get; set; } // Heat value to cook current item. 13 | public string sessionId { get; set; } // See crafting variant 14 | public string recipeId { get; set; } // See crafting variant 15 | public RecipeOutput output { get; set; } // See crafting variant 16 | public InputItem[] escrow { get; set; } // See crafting variant 17 | public int completed { get; set; } // See crafting variant 18 | public int available { get; set; } // See crafting variant 19 | public int total { get; set; } // See crafting variant 20 | public DateTime? nextCompletionUtc { get; set; } // See crafting variant 21 | public DateTime? totalCompletionUtc { get; set; } // See crafting variant 22 | public string state { get; set; } // Active, completed, or locked (or Empty) 23 | public BoostState boostState { get; set; } // See crafting variant 24 | public UnlockPrice unlockPrice { get; set; } // See crafting variant 25 | public ulong streamVersion { get; set; } // See crafting variant 26 | } 27 | 28 | public class SmeltingSlotResponse 29 | { 30 | public SmeltingSlotInfo result { get; set; } 31 | public Updates updates { get; set; } 32 | } 33 | 34 | public class BurningItems 35 | { 36 | public DateTime? burnStartTime { get; set; } // When the items started burning 37 | public DateTime? burnsUntil { get; set; } // How long the items are burning 38 | public FuelInfo fuel { get; set; } // What is burning 39 | public double heatDepleted { get; set; } // How much heat has been generated 40 | public TimeSpan remainingBurnTime { get; set; } // Burning time remaining 41 | } 42 | 43 | public class FuelInfo 44 | { 45 | public BurnInfo burnRate { get; set; } // How fast the items are burning 46 | public Guid itemId { get; set; } // Item ID of fuel 47 | public InventoryResponse.ItemInstance[] itemInstanceIds { get; set; } // Instance of the fuel item (Probably used when burning tools 48 | public int quantity { get; set; } // How many items are used as fuel 49 | } 50 | 51 | public class BurnInfo 52 | { 53 | public int burnTime { get; set; } // How long one item burns 54 | public int heatPerSecond { get; set; } // How much heat it generates 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Features/UtilityBlocksResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using ProjectEarthServerAPI.Util; 5 | 6 | namespace ProjectEarthServerAPI.Models.Features 7 | { 8 | public class UtilityBlocksResponse 9 | { 10 | public Result result { get; set; } 11 | public Updates updates { get; set; } 12 | 13 | public class Result 14 | { 15 | public Dictionary crafting { get; set; } 16 | public Dictionary smelting { get; set; } 17 | } 18 | 19 | public UtilityBlocksResponse() 20 | { 21 | result = new Result {crafting = new Dictionary(), smelting = new Dictionary()}; 22 | 23 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 24 | 25 | result.crafting.Add("1", new CraftingSlotInfo()); 26 | result.crafting.Add("2", new CraftingSlotInfo()); 27 | result.crafting.Add("3", new CraftingSlotInfo()); 28 | 29 | result.smelting.Add("1", new SmeltingSlotInfo()); 30 | result.smelting.Add("2", new SmeltingSlotInfo()); 31 | result.smelting.Add("3", new SmeltingSlotInfo()); 32 | 33 | result.crafting["1"].state = "Empty"; 34 | 35 | result.crafting["2"].state = "Locked"; 36 | result.crafting["2"].unlockPrice = new UnlockPrice {cost = 10, discount = 0}; 37 | result.crafting["3"].state = "Locked"; 38 | result.crafting["3"].unlockPrice = new UnlockPrice {cost = 15, discount = 1}; 39 | 40 | result.crafting["1"].escrow = new InputItem[0]; 41 | result.crafting["2"].escrow = new InputItem[0]; 42 | result.crafting["3"].escrow = new InputItem[0]; 43 | 44 | result.crafting["1"].streamVersion = nextStreamId; 45 | result.crafting["2"].streamVersion = nextStreamId; 46 | result.crafting["3"].streamVersion = nextStreamId; 47 | 48 | 49 | result.smelting["1"].state = "Empty"; 50 | 51 | result.smelting["2"].state = "Locked"; 52 | result.smelting["2"].unlockPrice = new UnlockPrice {cost = 10, discount = 0}; 53 | result.smelting["3"].state = "Locked"; 54 | result.smelting["3"].unlockPrice = new UnlockPrice {cost = 15, discount = 1}; 55 | 56 | result.smelting["1"].escrow = new InputItem[0]; 57 | result.smelting["2"].escrow = new InputItem[0]; 58 | result.smelting["3"].escrow = new InputItem[0]; 59 | 60 | result.smelting["1"].streamVersion = nextStreamId; 61 | result.smelting["2"].streamVersion = nextStreamId; 62 | result.smelting["3"].streamVersion = nextStreamId; 63 | 64 | updates = new Updates(); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Login/FeaturesResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectEarthServerAPI.Models 2 | { 3 | public class FeaturesResponse 4 | { 5 | public FeaturesResult result { get; set; } 6 | public string expiration { get; set; } 7 | public string continuationTokem { get; set; } 8 | public Updates updates { get; set; } 9 | } 10 | 11 | public class FeaturesResult 12 | { 13 | public bool workshop_enabled { get; set; } = true; 14 | public bool buildplates_enabled { get; set; } = true; 15 | public bool enable_ruby_purchasing { get; set; } 16 | public bool commerce_enabled { get; set; } = true; 17 | public bool full_logging_enabled { get; set; } = true; 18 | public bool challenges_enabled { get; set; } = true; 19 | public bool craftingv2_enabled { get; set; } = true; 20 | public bool smeltingv2_enabled { get; set; } = true; 21 | public bool inventory_item_boosts_enabled { get; set; } = true; 22 | public bool player_health_enabled { get; set; } = true; 23 | public bool minifigs_enabled { get; set; } = true; 24 | public bool potions_enabled { get; set; } = true; 25 | public bool social_link_share_enabled { get; set; } = true; 26 | public bool social_link_launch_enabled { get; set; } = true; 27 | public bool encoded_join_enabled { get; set; } = true; 28 | public bool adventure_crystals_enabled { get; set; } = true; 29 | public bool item_limits_enabled { get; set; } = true; 30 | public bool adventure_crystals_ftue_enabled { get; set; } = true; 31 | public bool expire_crystals_on_cleanup_enabled { get; set; } = true; 32 | public bool challenges_v2_enabled { get; set; } = true; 33 | public bool player_journal_enabled { get; set; } = true; 34 | public bool player_stats_enabled { get; set; } = true; 35 | public bool activity_log_enabled { get; set; } = true; 36 | public bool seasons_enabled { get; set; } = true; 37 | public bool daily_login_enabled { get; set; } = true; 38 | public bool store_pdp_enabled { get; set; } = true; 39 | public bool hotbar_stacksplitting_enabled { get; set; } = true; 40 | public bool fancy_rewards_screen_enabled { get; set; } = true; 41 | public bool async_ecs_dispatcher { get; set; } = true; 42 | public bool adventure_oobe_enabled { get; set; } = true; 43 | public bool tappable_oobe_enabled { get; set; } = true; 44 | public bool map_permission_oobe_enabled { get; set; } = true; 45 | public bool journal_oobe_enabled { get; set; } = true; 46 | public bool freedom_oobe_enabled { get; set; } = true; 47 | public bool challenge_oobe_enabled { get; set; } = true; 48 | public bool level_rewards_v2_enabled { get; set; } = true; 49 | public bool content_driven_season_assets { get; set; } = true; 50 | public bool paid_earned_rubies_enabled { get; set; } = true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Login/LocatorResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | 7 | namespace ProjectEarthServerAPI.Models 8 | { 9 | public class LocatorResponse 10 | { 11 | public class Production 12 | { 13 | [JsonProperty(Order = 1)] 14 | public string serviceUri { get; set; } 15 | 16 | [JsonProperty(Order = 2)] 17 | public string cdnUri { get; set; } 18 | 19 | [JsonProperty(Order = 3)] 20 | public string playfabTitleId { get; set; } 21 | } 22 | 23 | public class ServiceEnvironments 24 | { 25 | public Production production { get; set; } 26 | } 27 | 28 | public class Result 29 | { 30 | public ServiceEnvironments serviceEnvironments { get; set; } 31 | public Dictionary> supportedEnvironments { get; set; } 32 | } 33 | 34 | public class Root 35 | { 36 | public Result result { get; set; } 37 | public object expiration { get; set; } 38 | public object continuationToken { get; set; } 39 | public Updates updates { get; set; } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Login/SettingsResponse.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Newtonsoft.Json; 3 | 4 | namespace ProjectEarthServerAPI.Models 5 | { 6 | public class SettingsResponse 7 | { 8 | public SettingsResult result { get; set; } 9 | 10 | public static SettingsResponse FromFile(string path) 11 | { 12 | var jsontext = File.ReadAllText(path); 13 | return JsonConvert.DeserializeObject(jsontext); 14 | } 15 | } 16 | 17 | public class SettingsResult 18 | { 19 | public double encounterinteractionradius { get; set; } 20 | public double tappableinteractionradius { get; set; } 21 | public double tappablevisibleradius { get; set; } 22 | public double targetpossibletappables { get; set; } 23 | public double tile0 { get; set; } 24 | public double slowrequesttimeout { get; set; } 25 | public double cullingradius { get; set; } 26 | public double commontapcount { get; set; } 27 | public double epictapcount { get; set; } 28 | public double speedwarningcooldown { get; set; } 29 | public double mintappablesrequiredpertile { get; set; } 30 | public double targetactivetappables { get; set; } 31 | public double tappablecullingradius { get; set; } 32 | public double raretapcount { get; set; } 33 | public double requestwarningtimeout { get; set; } 34 | public double speedwarningthreshold { get; set; } 35 | public double asaanchormaxplaneheightthreshold { get; set; } 36 | public double maxannouncementscount { get; set; } 37 | public double removethislater { get; set; } 38 | public double crystalslotcap { get; set; } 39 | public double crystaluncommonduration { get; set; } 40 | public double crystalrareduration { get; set; } 41 | public double crystalepicduration { get; set; } 42 | public double crystalcommonduration { get; set; } 43 | public double crystallegendaryduration { get; set; } 44 | public double maximumpersonaltimedchallenges { get; set; } 45 | public double maximumpersonalcontinuouschallenges { get; set; } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Login/SigninRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ProjectEarthServerAPI.Models 7 | { 8 | public class Coordinate 9 | { 10 | public double latitude { get; set; } 11 | public double longitude { get; set; } 12 | } 13 | 14 | public class SigninRequest 15 | { 16 | public string advertisingId { get; set; } 17 | public string appsFlyerId { get; set; } 18 | public string buildNumber { get; set; } 19 | public string clientVersion { get; set; } 20 | public Coordinate coordinate { get; set; } 21 | public string deviceId { get; set; } 22 | public string deviceOS { get; set; } 23 | public string deviceToken { get; set; } 24 | public string language { get; set; } 25 | public string sessionTicket { get; set; } 26 | public object streams { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Login/SigninResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | using Uma.Uuid; 4 | 5 | namespace ProjectEarthServerAPI.Models 6 | { 7 | public class SigninResponse 8 | { 9 | [JsonObject(ItemNullValueHandling = NullValueHandling.Include)] 10 | public class ResponseTemplate 11 | { 12 | public Result result { get; set; } 13 | public Updates updates { get; set; } 14 | } 15 | 16 | [JsonObject(ItemNullValueHandling = NullValueHandling.Include)] 17 | public class Result 18 | { 19 | [JsonProperty("authenticationToken")] 20 | public string AuthenticationToken { get; set; } 21 | 22 | [JsonProperty("basePath")] 23 | public string BasePath { get; set; } 24 | 25 | [JsonProperty("clientProperties")] 26 | public object ClientProperties { get; set; } 27 | 28 | [JsonProperty("mixedReality")] 29 | public object MixedReality { get; set; } 30 | 31 | [JsonProperty("mrToken")] 32 | public object MrToken { get; set; } 33 | 34 | [JsonProperty("streams")] 35 | public object Streams { get; set; } 36 | 37 | [JsonProperty("tokens")] 38 | public Dictionary Tokens { get; set; } 39 | 40 | [JsonProperty("updates")] 41 | public Updates Updates { get; set; } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Adventure/AdventureRequestResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Converters; 4 | 5 | namespace ProjectEarthServerAPI.Models.Multiplayer.Adventure 6 | { 7 | public class AdventureRequestResult 8 | { 9 | public LocationResponse.ActiveLocation result { get; set; } 10 | public Updates updates { get; set; } 11 | } 12 | 13 | public class EncounterMetadata 14 | { 15 | public string anchorId { get; set; } 16 | public string anchorState { get; set; } 17 | public string augmentedImageSetId { get; set; } 18 | 19 | [JsonConverter(typeof(StringEnumConverter))] 20 | public EncounterType encounterType { get; set; } 21 | 22 | public Guid locationId { get; set; } 23 | public Guid worldId { get; set; } 24 | } 25 | 26 | public enum EncounterType 27 | { 28 | None 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Adventure/PlayerAdventureRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using ProjectEarthServerAPI.Models.Player; 3 | 4 | namespace ProjectEarthServerAPI.Models.Multiplayer.Adventure 5 | { 6 | public class PlayerAdventureRequest 7 | { 8 | public Coordinate coordinate { get; set; } 9 | public InventoryResponse.Hotbar[] hotbar { get; set; } 10 | public Guid? instanceId { get; set; } 11 | public Guid[] scrollsToDeactivate { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Buildplate/BuildplateListResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Converters; 7 | using ProjectEarthServerAPI.Util; 8 | 9 | namespace ProjectEarthServerAPI.Models.Buildplate 10 | { 11 | /// 12 | /// The list of buildplates that show up on the buildplate sceen. 13 | /// 14 | public class BuildplateListResponse 15 | { 16 | public object continuationToken { get; set; } 17 | public object expiration { get; set; } 18 | public List result { get; set; } 19 | public Updates updates { get; set; } 20 | } 21 | 22 | public class BuildplateData 23 | { 24 | public double blocksPerMeter { get; set; } 25 | public Dimension dimension { get; set; } 26 | public string eTag { get; set; } 27 | public Guid id { get; set; } 28 | public bool isModified { get; set; } 29 | 30 | [JsonConverter(typeof(DateTimeConverter))] 31 | public DateTime lastUpdated { get; set; } 32 | 33 | public bool locked { get; set; } 34 | public string model { get; set; } 35 | public int numberOfBlocks { get; set; } 36 | public Offset offset { get; set; } 37 | public int order { get; set; } 38 | public int requiredLevel { get; set; } 39 | 40 | [JsonConverter(typeof(StringEnumConverter))] 41 | public SurfaceOrientation surfaceOrientation { get; set; } 42 | 43 | public Guid templateId { get; set; } 44 | public string type { get; set; } 45 | } 46 | 47 | public class Dimension 48 | { 49 | public int x { get; set; } 50 | public int z { get; set; } 51 | } 52 | 53 | public class Offset 54 | { 55 | public double x { get; set; } 56 | public double y { get; set; } 57 | public double z { get; set; } 58 | } 59 | 60 | public class BuildplateShareResponse 61 | { 62 | public BuildplateShareInfo result { get; set; } 63 | 64 | public class BuildplateShareInfo 65 | { 66 | public string playerId { get; set; } 67 | public BuildplateData buildplateData { get; set; } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Buildplate/BuildplateServerRequest.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectEarthServerAPI.Models.Buildplate 2 | { 3 | public class BuildplateServerRequest 4 | { 5 | public double coordinateAccuracyVariance { get; set; } 6 | public Coordinate playerCoordinate { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Buildplate/BuildplateServerResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Converters; 7 | 8 | namespace ProjectEarthServerAPI.Models.Buildplate 9 | { 10 | public class BuildplateServerResponse 11 | { 12 | public class BreakableItemToItemLootMap { } 13 | 14 | public class SaveState 15 | { 16 | public bool boosts { get; set; } 17 | public bool experiencePoints { get; set; } 18 | public bool health { get; set; } 19 | public bool inventory { get; set; } 20 | public bool model { get; set; } 21 | public bool world { get; set; } 22 | } 23 | 24 | public class SnapshotOptions 25 | { 26 | public SaveState saveState { get; set; } 27 | public string snapshotTriggerConditions { get; set; } 28 | public string snapshotWorldStorage { get; set; } 29 | public List triggerConditions { get; set; } 30 | public TimeSpan triggerInterval { get; set; } 31 | } 32 | 33 | public class GameplayMetadata 34 | { 35 | public object augmentedImageSetId { get; set; } 36 | public double blocksPerMeter { get; set; } 37 | public BreakableItemToItemLootMap breakableItemToItemLootMap { get; set; } 38 | public Dimension dimension { get; set; } 39 | 40 | [JsonConverter(typeof(StringEnumConverter))] 41 | public GameplayMode gameplayMode { get; set; } 42 | 43 | public bool isFullSize { get; set; } 44 | public Offset offset { get; set; } 45 | public string playerJoinCode { get; set; } 46 | public object rarity { get; set; } 47 | public List shutdownBehavior { get; set; } 48 | public SnapshotOptions snapshotOptions { get; set; } 49 | public string spawningClientBuildNumber { get; set; } 50 | public string spawningPlayerId { get; set; } 51 | 52 | [JsonConverter(typeof(StringEnumConverter))] 53 | public SurfaceOrientation surfaceOrientation { get; set; } 54 | 55 | public Guid templateId { get; set; } 56 | public string worldId { get; set; } 57 | } 58 | 59 | public class Result 60 | { 61 | public string applicationStatus { get; set; } 62 | public string fqdn { get; set; } 63 | public GameplayMetadata gameplayMetadata { get; set; } 64 | public Coordinate hostCoordinate { get; set; } 65 | public string instanceId { get; set; } 66 | public string ipV4Address { get; set; } 67 | public string metadata { get; set; } 68 | public string partitionId { get; set; } 69 | public int port { get; set; } 70 | public string roleInstance { get; set; } 71 | public bool serverReady { get; set; } 72 | public string serverStatus { get; set; } 73 | } 74 | 75 | public object continuationToken { get; set; } 76 | public object expiration { get; set; } 77 | public Result result { get; set; } 78 | public Updates updates { get; set; } 79 | 80 | public class InstanceMetadata 81 | { 82 | public string buildplateid { get; set; } 83 | } 84 | } 85 | 86 | public enum GameplayMode 87 | { 88 | Buildplate, 89 | Encounter 90 | } 91 | 92 | public enum SurfaceOrientation 93 | { 94 | Horizontal, 95 | Vertical 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/Buildplate/PlayerBuildplateList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace ProjectEarthServerAPI.Models.Buildplate 5 | { 6 | public class PlayerBuildplateList 7 | { 8 | public List UnlockedBuildplates { get; set; } 9 | public List LockedBuildplates { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/MultiplayerInventoryResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Microsoft.IdentityModel.Tokens; 5 | using Newtonsoft.Json; 6 | using ProjectEarthServerAPI.Models.Features; 7 | using ProjectEarthServerAPI.Models.Player; 8 | using ProjectEarthServerAPI.Util; 9 | 10 | namespace ProjectEarthServerAPI.Models.Multiplayer 11 | { 12 | public class MultiplayerInventoryResponse 13 | { 14 | public MultiplayerItem[] hotbar { get; set; } 15 | 16 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 17 | public MultiplayerItem[] inventory { get; set; } 18 | } 19 | 20 | public class MultiplayerItem 21 | { 22 | [JsonConverter(typeof(MultiplayerItemCategoryConverter))] 23 | public MultiplayerItemCategory category { get; set; } 24 | 25 | public int count { get; set; } 26 | public Guid guid { get; set; } 27 | 28 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 29 | public InventoryResponse.ItemInstance instance_data { get; set; } 30 | 31 | public bool owned { get; set; } 32 | 33 | [JsonConverter(typeof(MultiplayerItemRarityConverter))] 34 | public MultiplayerItemRarity rarity { get; set; } 35 | 36 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 37 | public MultiplayerItemUnlocked unlocked { get; set; } 38 | 39 | public static MultiplayerItem ConvertToMultiplayerItem(InventoryResponse.Hotbar item) 40 | { 41 | if (item != null) return ConvertToMultiplayerItem(item.id, item.count, true, null /*InventoryUtils.GetInstance(item.instanceId)*/); //TODO: Implement GetInstance()! 42 | 43 | var multiplayerItem = new MultiplayerItem 44 | { 45 | category = new MultiplayerItemCategory {loc = ItemCategory.Invalid, value = (int)ItemCategory.Invalid}, 46 | count = 0, 47 | guid = Guid.Parse("00000000-0000-0000-0000-000000000000"), 48 | owned = false, 49 | rarity = new MultiplayerItemRarity {loc = ItemRarity.Invalid, value = 6} 50 | }; 51 | 52 | return multiplayerItem; 53 | } 54 | 55 | public static MultiplayerItem ConvertToMultiplayerItem(InventoryResponse.StackableItem item) => 56 | ConvertToMultiplayerItem(item.id, item.owned, true, item.unlocked.on); 57 | 58 | public static List ConvertToMultiplayerItems(InventoryResponse.NonStackableItem item) 59 | { 60 | return item.instances 61 | .Select(instance => ConvertToMultiplayerItem(item.id, item.instances.Count, true, item.unlocked.@on, instance)) 62 | .ToList(); 63 | } 64 | 65 | private static MultiplayerItem ConvertToMultiplayerItem(Guid itemId, int count, bool owned, DateTime? unlockedOn = null, InventoryResponse.ItemInstance itemInstance = null) 66 | { 67 | var catalogItem = StateSingleton.Instance.catalog.result.items.Find(match => match.id == itemId); 68 | ItemCategory category = 69 | Enum.TryParse(catalogItem.category, true, out ItemCategory itemCategory) 70 | ? itemCategory 71 | : ItemCategory.Invalid; 72 | 73 | ItemRarity rarity = Enum.TryParse(Enum.GetName(catalogItem.rarity), true, out ItemRarity itemRarity) 74 | ? itemRarity 75 | : ItemRarity.Invalid; 76 | 77 | var multiplayerItem = new MultiplayerItem 78 | { 79 | count = count, 80 | guid = itemId, 81 | owned = owned, 82 | category = new MultiplayerItemCategory {loc = category, value = (int)category}, 83 | rarity = new MultiplayerItemRarity {loc = rarity, value = (int)rarity} 84 | }; 85 | 86 | if (unlockedOn != null) 87 | { 88 | multiplayerItem.unlocked = new MultiplayerItemUnlocked {on = EpochTime.GetIntDate(unlockedOn.Value)}; 89 | } 90 | 91 | if (itemInstance != null) 92 | { 93 | multiplayerItem.instance_data = itemInstance; 94 | } 95 | 96 | return multiplayerItem; 97 | } 98 | } 99 | 100 | public class MultiplayerItemCategory 101 | { 102 | public ItemCategory loc { get; set; } 103 | public int value { get; set; } 104 | } 105 | 106 | public enum ItemCategory 107 | { 108 | Mobs = 1, 109 | Construction, 110 | Nature, 111 | Equipment, 112 | Items, 113 | Invalid 114 | } 115 | 116 | public class MultiplayerItemRarity 117 | { 118 | public ItemRarity loc { get; set; } 119 | public int value { get; set; } 120 | } 121 | 122 | public enum ItemRarity 123 | { 124 | Common = 0, 125 | Uncommon, 126 | Rare, 127 | Epic, 128 | Legendary, 129 | Invalid = 6 130 | } 131 | 132 | public class MultiplayerItemUnlocked 133 | { 134 | public long on { get; set; } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/ServerAuthInformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ProjectEarthServerAPI.Models.Multiplayer 4 | { 5 | public enum ServerAuthInformation 6 | { 7 | NotAuthed, 8 | AuthStage1, 9 | AuthStage2, 10 | Authed, 11 | FailedAuth 12 | } 13 | 14 | public class ServerInformation 15 | { 16 | public Guid serverId { get; set; } 17 | public string ip { get; set; } 18 | public int port { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Multiplayer/ServerCommandRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Data; 3 | using Newtonsoft.Json; 4 | 5 | namespace ProjectEarthServerAPI.Models.Multiplayer 6 | { 7 | public class ServerCommandRequest 8 | { 9 | public ServerCommandType command { get; set; } 10 | public string playerId { get; set; } 11 | public Guid apiKey { get; set; } 12 | public Guid serverId { get; set; } 13 | public string requestData { get; set; } 14 | } 15 | 16 | public class BuildplateRequest 17 | { 18 | public Guid buildplateId { get; set; } 19 | public string playerId { get; set; } 20 | } 21 | 22 | public class HotbarTranslation 23 | { 24 | public string identifier { get; set; } 25 | public int meta { get; set; } 26 | public int count { get; set; } 27 | public int slotId { get; set; } 28 | } 29 | 30 | public class EditInventoryRequest 31 | { 32 | public string identifier { get; set; } 33 | public int meta { get; set; } 34 | public int count { get; set; } 35 | public int slotIndex { get; set; } 36 | public float health { get; set; } 37 | public bool removeItem { get; set; } 38 | } 39 | 40 | public class ServerInstanceInfo 41 | { 42 | public Guid instanceId { get; set; } 43 | public Guid buildplateId { get; set; } 44 | } 45 | 46 | public class ServerInstanceRequestInfo 47 | { 48 | public Guid instanceId { get; set; } 49 | public Guid buildplateId { get; set; } 50 | public string playerId { get; set; } 51 | } 52 | 53 | public enum ServerCommandType 54 | { 55 | GetInventory, 56 | GetInventoryForClient, 57 | EditInventory, 58 | EditHotbar, 59 | GetBuildplate, 60 | EditBuildplate, 61 | MarkServerAsReady 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/BoostsResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json; 5 | using ProjectEarthServerAPI.Models.Features; 6 | 7 | namespace ProjectEarthServerAPI.Models 8 | { 9 | public class MiniFigRecords { } 10 | 11 | public class StatusEffects 12 | { 13 | public int? tappableInteractionRadius { get; set; } 14 | public int? experiencePointRate { get; set; } 15 | public List itemExperiencePointRates { get; set; } 16 | public int? attackDamageRate { get; set; } 17 | public int? playerDefenseRate { get; set; } 18 | public int? blockDamageRate { get; set; } 19 | public int? maximumPlayerHealth { get; set; } 20 | public int? craftingSpeed { get; set; } 21 | public int? smeltingFuelIntensity { get; set; } 22 | public int? foodHealthRate { get; set; } 23 | } 24 | 25 | public class ScenarioBoosts 26 | { 27 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 28 | public List death { get; set; } // Only on death for now 29 | } 30 | 31 | public class ActiveBoost 32 | { 33 | public List effects { get; set; } 34 | public bool enabled { get; set; } 35 | public DateTime? expiration { get; set; } 36 | public string instanceId { get; set; } 37 | } 38 | 39 | public class BoostResult 40 | { 41 | public List potions { get; set; } 42 | 43 | public List miniFigs { get; set; } 44 | 45 | public MiniFigRecords miniFigRecords { get; set; } 46 | 47 | public List activeEffects { get; set; } 48 | 49 | public StatusEffects statusEffects { get; set; } 50 | 51 | public ScenarioBoosts scenarioBoosts { get; set; } 52 | 53 | public DateTime? expiration { get; set; } 54 | } 55 | 56 | public class Potion 57 | { 58 | public bool enabled { get; set; } 59 | public DateTime? expiration { get; set; } 60 | public string instanceId { get; set; } 61 | public Guid itemId { get; set; } 62 | } 63 | 64 | public class ActiveEffect 65 | { 66 | public Item.Effect effect { get; set; } 67 | public DateTime? expiration { get; set; } 68 | } 69 | 70 | public class BoostResponse 71 | { 72 | public BoostResult result { get; set; } 73 | public Updates updates { get; set; } 74 | 75 | public BoostResponse() 76 | { 77 | result = new BoostResult 78 | { 79 | activeEffects = new List(), 80 | miniFigRecords = new MiniFigRecords(), 81 | miniFigs = new List(), 82 | potions = new List(), 83 | scenarioBoosts = new ScenarioBoosts(), 84 | statusEffects = new StatusEffects {itemExperiencePointRates = new List()} 85 | }; 86 | updates = new Updates(); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/ChallengesResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Converters; 6 | using ProjectEarthServerAPI.Util; 7 | using Uma.Uuid; 8 | 9 | namespace ProjectEarthServerAPI.Models.Player 10 | { 11 | public class ChallengesResponse 12 | { 13 | public ChallengesList result { get; set; } 14 | public Updates updates { get; set; } 15 | 16 | public static ChallengesResponse FromFile(string path) 17 | { 18 | var jsontext = File.ReadAllText(path); 19 | return JsonConvert.DeserializeObject(jsontext); 20 | } 21 | 22 | public ChallengesResponse() 23 | { 24 | if (StateSingleton.Instance.seasonChallenges != null) 25 | result = StateSingleton.Instance.seasonChallenges.result; 26 | } 27 | } 28 | 29 | public class ChallengesList 30 | { 31 | public Dictionary challenges { get; set; } 32 | public Guid activeSeasonChallenge { get; set; } 33 | } 34 | 35 | public class ChallengeInfo 36 | { 37 | public Guid referenceId { get; set; } 38 | 39 | [JsonConverter(typeof(StringEnumConverter))] 40 | public ChallengeDuration duration { get; set; } 41 | 42 | [JsonConverter(typeof(StringEnumConverter))] 43 | public ChallengeType type { get; set; } 44 | 45 | public DateTime? endTimeUtc { get; set; } 46 | public Rewards rewards { get; set; } 47 | public int percentComplete { get; set; } 48 | public bool isComplete { get; set; } 49 | 50 | [JsonConverter(typeof(StringEnumConverter))] 51 | public ChallengeState state { get; set; } 52 | 53 | [JsonConverter(typeof(StringEnumConverter))] 54 | public ChallengeCategory category { get; set; } 55 | 56 | public int currentCount { get; set; } 57 | public int totalThreshold { get; set; } 58 | public Guid? parentId { get; set; } 59 | public int order { get; set; } 60 | public string rarity { get; set; } 61 | 62 | [JsonConverter(typeof(StringEnumConverter))] 63 | public ChallengeLogicCondition prerequisiteLogicalCondition { get; set; } 64 | 65 | public List prerequisiteIds { get; set; } 66 | public Guid groupId { get; set; } 67 | public ChallengeProperties clientProperties { get; set; } 68 | } 69 | 70 | public class ChallengeProperties 71 | { 72 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 73 | public string topDecorationTexture { get; set; } 74 | 75 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 76 | public string challengeCollectedAudioEvent { get; set; } 77 | 78 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 79 | public string challengeSelectedAudioEvent { get; set; } 80 | 81 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] 82 | public string frameTexture { get; set; } 83 | } 84 | 85 | public enum ChallengeDuration 86 | { 87 | Career, 88 | Season, 89 | SignIn, 90 | OOBE, 91 | PersonalTimed, 92 | PersonalContinuous 93 | } 94 | 95 | public enum ChallengeType 96 | { 97 | Journal, 98 | Regular 99 | } 100 | 101 | public enum ChallengeState 102 | { 103 | Active, 104 | Completed, 105 | Locked 106 | } 107 | 108 | public enum ChallengeCategory 109 | { 110 | Building, 111 | Collection, 112 | cow, 113 | chicken, 114 | oobe, 115 | pig, 116 | rabbit, 117 | retention, 118 | season_17, 119 | sheep, 120 | Smelting 121 | } 122 | 123 | public enum ChallengeLogicCondition 124 | { 125 | And, 126 | Or 127 | } 128 | 129 | public enum ChallengeEventType 130 | { 131 | ItemAwarded, 132 | TappableRedeemed, 133 | MobKilled 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/InventoryResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Policy; 5 | using System.Threading.Tasks; 6 | 7 | namespace ProjectEarthServerAPI.Models.Player 8 | { 9 | /// 10 | /// Models the player inventory endpoint (inventory/survival) 11 | /// 12 | public class InventoryResponse 13 | { 14 | #region Model 15 | 16 | public Result result { get; set; } 17 | public object expiration { get; set; } 18 | public object continuationToken { get; set; } 19 | public Updates updates { get; set; } 20 | 21 | public class Hotbar 22 | { 23 | //I havent done adventures recently. Health is **Probably wrong** 24 | public Guid id { get; set; } 25 | public Guid? instanceId { get; set; } 26 | public int count { get; set; } 27 | } 28 | 29 | public class ItemInstance 30 | { 31 | public Guid id { get; set; } 32 | public double health { get; set; } 33 | } 34 | 35 | public class DateTimeOn 36 | { 37 | public DateTime on { get; set; } 38 | } 39 | 40 | public class BaseItem 41 | { 42 | public Guid id { get; set; } // Item UUID 43 | public DateTimeOn seen { get; set; } // When you last used/got this item 44 | public DateTimeOn unlocked { get; set; } // When you first unlocked the item 45 | public int fragments { get; set; } // Not used 46 | } 47 | 48 | public class StackableItem : BaseItem 49 | { 50 | public int owned { get; set; } // How many you have 51 | } 52 | 53 | public class NonStackableItem : BaseItem 54 | { 55 | public List instances { get; set; } // List of Instances, see above explanation 56 | } 57 | 58 | public class Result 59 | { 60 | public Hotbar[] hotbar { get; set; } // Items you have in your hotbar 61 | public List stackableItems { get; set; } // Stackable items (dirt, cobble, torches) 62 | public List nonStackableItems { get; set; } // Unstackable items (picks, axes, swords) 63 | } 64 | 65 | #endregion 66 | 67 | #region Functions 68 | 69 | public InventoryResponse() 70 | { 71 | result = new Result {hotbar = new Hotbar[7], nonStackableItems = new List(1), stackableItems = new List(1)}; 72 | } 73 | 74 | #endregion 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/LocationResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Converters; 7 | using ProjectEarthServerAPI.Models.Features; 8 | using ProjectEarthServerAPI.Models.Multiplayer.Adventure; 9 | using ProjectEarthServerAPI.Util; 10 | 11 | namespace ProjectEarthServerAPI.Models 12 | { 13 | public class LocationResponse 14 | { 15 | public class Metadata : TappableMetadata 16 | { 17 | public string rewardId { get; set; } 18 | } 19 | 20 | public class TappableMetadata 21 | { 22 | [JsonConverter(typeof(StringEnumConverter))] 23 | public Item.Rarity rarity { get; set; } 24 | } 25 | 26 | public class ActiveLocation 27 | { 28 | public string id { get; set; } 29 | public string tileId { get; set; } 30 | public Coordinate coordinate { get; set; } 31 | 32 | [JsonConverter(typeof(DateTimeConverter))] 33 | public DateTime spawnTime { get; set; } 34 | 35 | [JsonConverter(typeof(DateTimeConverter))] 36 | public DateTime expirationTime { get; set; } 37 | 38 | public string type { get; set; } 39 | public string icon { get; set; } 40 | public Metadata metadata { get; set; } 41 | public EncounterMetadata encounterMetadata { get; set; } 42 | public TappableMetadata tappableMetadata { get; set; } 43 | } 44 | 45 | public class Result 46 | { 47 | public List killSwitchedTileIds { get; set; } 48 | public List activeLocations { get; set; } 49 | } 50 | 51 | public class Root 52 | { 53 | public Result result { get; set; } 54 | public object expiration { get; set; } 55 | public object continuationToken { get; set; } 56 | public Updates updates { get; set; } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/ProfileResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace ProjectEarthServerAPI.Models 6 | { 7 | public class ProfileResponse 8 | { 9 | public static Dictionary levels { get; private set; } 10 | 11 | // Setup the level distribution dict when the class is first called 12 | static ProfileResponse() 13 | { 14 | levels = new Dictionary(); 15 | levels.Add("2", new ProfileLevel {experienceRequired = 500, rewards = new Rewards {Rubies = 15, Inventory = new RewardComponent[] {new RewardComponent {Id = new Guid("730573d1-ba59-4fd4-89e0-85d4647466c2"), Amount = 1}, new RewardComponent {Id = new Guid("20dbd5fc-06b7-1aa1-5943-7ddaa2061e6a"), Amount = 8}, new RewardComponent {Id = new Guid("1eaa0d8c-2d89-2b84-aa1f-b75ccc85faff"), Amount = 64}}}}); 16 | levels.Add("3", new ProfileLevel {experienceRequired = 1500}); 17 | levels.Add("4", new ProfileLevel {experienceRequired = 2800}); 18 | levels.Add("5", new ProfileLevel {experienceRequired = 4600}); 19 | levels.Add("6", new ProfileLevel {experienceRequired = 6100}); 20 | levels.Add("7", new ProfileLevel {experienceRequired = 7800}); 21 | levels.Add("8", new ProfileLevel {experienceRequired = 10100}); 22 | levels.Add("9", new ProfileLevel {experienceRequired = 13300}); 23 | levels.Add("10", new ProfileLevel {experienceRequired = 17800}); 24 | levels.Add("11", new ProfileLevel {experienceRequired = 21400}); 25 | levels.Add("12", new ProfileLevel {experienceRequired = 25700}); 26 | levels.Add("13", new ProfileLevel {experienceRequired = 31300}); 27 | levels.Add("14", new ProfileLevel {experienceRequired = 39100}); 28 | levels.Add("15", new ProfileLevel {experienceRequired = 50000}); 29 | levels.Add("16", new ProfileLevel {experienceRequired = 58700}); 30 | levels.Add("17", new ProfileLevel {experienceRequired = 68700}); 31 | levels.Add("18", new ProfileLevel {experienceRequired = 82700}); 32 | levels.Add("19", new ProfileLevel {experienceRequired = 101700}); 33 | levels.Add("20", new ProfileLevel {experienceRequired = 128700}); 34 | levels.Add("21", new ProfileLevel {experienceRequired = 137400}); 35 | levels.Add("22", new ProfileLevel {experienceRequired = 147000}); 36 | levels.Add("23", new ProfileLevel {experienceRequired = 157000}); 37 | levels.Add("24", new ProfileLevel {experienceRequired = 169000}); 38 | levels.Add("25", new ProfileLevel {experienceRequired = 185000}); 39 | } 40 | 41 | public ProfileResult result { get; set; } 42 | public object continuationToken { get; set; } 43 | public object expiration { get; set; } 44 | public Updates updates { get; set; } 45 | 46 | public ProfileResponse(ProfileData profileData) 47 | { 48 | result = ProfileResult.of(profileData); 49 | } 50 | } 51 | 52 | public class ProfileResult : ProfileData 53 | { 54 | public Dictionary levelDistribution { get; set; } 55 | 56 | public static ProfileResult of(ProfileData profileData) 57 | { 58 | return new ProfileResult 59 | { 60 | totalExperience = profileData.totalExperience, 61 | level = profileData.level, 62 | health = profileData.health, 63 | healthPercentage = profileData.healthPercentage, 64 | levelDistribution = ProfileResponse.levels 65 | }; 66 | } 67 | } 68 | 69 | public class ProfileLevel 70 | { 71 | public int experienceRequired { get; set; } 72 | public Rewards rewards { get; set; } 73 | 74 | public ProfileLevel() 75 | { 76 | rewards = new Rewards(); 77 | } 78 | } 79 | 80 | public class ProfileData 81 | { 82 | public int totalExperience { get; set; } 83 | public int level { get; set; } 84 | 85 | public int currentLevelExperience 86 | { 87 | get 88 | { 89 | ProfileLevel profileLevel; 90 | if (ProfileResponse.levels.TryGetValue(level.ToString(), out profileLevel)) 91 | { 92 | return totalExperience - profileLevel.experienceRequired; 93 | } 94 | 95 | return totalExperience; 96 | } 97 | } 98 | 99 | public int experienceRemaining 100 | { 101 | get 102 | { 103 | ProfileLevel profileLevel; 104 | if (ProfileResponse.levels.TryGetValue((level + 1).ToString(), out profileLevel)) 105 | { 106 | return profileLevel.experienceRequired - currentLevelExperience; 107 | } 108 | 109 | return 0; 110 | } 111 | } 112 | 113 | public int? health { get; set; } 114 | public float healthPercentage { get; set; } 115 | 116 | public ProfileData() 117 | { 118 | totalExperience = 0; 119 | level = 1; 120 | healthPercentage = 100f; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/RubyResponse.cs: -------------------------------------------------------------------------------- 1 | namespace ProjectEarthServerAPI.Models 2 | { 3 | public class RubyResponse 4 | { 5 | public int result { get; set; } 6 | } 7 | 8 | public class SplitRubyResponse 9 | { 10 | public SplitRubyResult result { get; set; } 11 | 12 | public SplitRubyResponse() 13 | { 14 | result = new SplitRubyResult {earned = 100, purchased = 0}; 15 | } 16 | } 17 | 18 | public class SplitRubyResult 19 | { 20 | public int purchased { get; set; } 21 | public int earned { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/ScrollsResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace ProjectEarthServerAPI.Models 4 | { 5 | public class ScrollsResponse 6 | { 7 | public Array result { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/TappableRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ProjectEarthServerAPI.Models.Features 7 | { 8 | public class TappableRequest 9 | { 10 | public Guid id { get; set; } 11 | public Coordinate playerCoordinate { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/TappableResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace ProjectEarthServerAPI.Models.Player 7 | { 8 | public class TappableResponse 9 | { 10 | public Result result { get; set; } 11 | public object expiration { get; set; } 12 | public object continuationToken { get; set; } 13 | public Updates updates { get; set; } 14 | 15 | public class Result 16 | { 17 | public Token token { get; set; } 18 | public Updates updates { get; set; } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Player/TokenResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using Newtonsoft.Json; 5 | using Uma.Uuid; 6 | 7 | namespace ProjectEarthServerAPI.Models 8 | { 9 | public class TokenResponse 10 | { 11 | [JsonProperty("result")] 12 | public TokenResult Result { get; set; } 13 | 14 | public Updates updates { get; set; } 15 | 16 | public TokenResponse() 17 | { 18 | Result = new TokenResult {tokens = new Dictionary()}; 19 | } 20 | } 21 | 22 | public class TokenResult 23 | { 24 | public Dictionary tokens { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Rewards.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using Uma.Uuid; 4 | 5 | namespace ProjectEarthServerAPI.Models 6 | { 7 | public class Rewards 8 | { 9 | [JsonProperty("experiencePoints", NullValueHandling = NullValueHandling.Ignore)] 10 | public int? ExperiencePoints { get; set; } 11 | 12 | [JsonProperty("inventory")] 13 | public RewardComponent[] Inventory { get; set; } 14 | 15 | [JsonProperty("rubies", NullValueHandling = NullValueHandling.Ignore)] 16 | public int? Rubies { get; set; } 17 | 18 | [JsonProperty("buildplates")] 19 | public RewardComponent[] Buildplates { get; set; } 20 | 21 | [JsonProperty("challenges")] 22 | public RewardComponent[] Challenges { get; set; } 23 | 24 | [JsonProperty("personaItems")] 25 | public Uuid[] PersonaItems { get; set; } 26 | 27 | [JsonProperty("utilityBlocks")] 28 | public RewardComponent[] UtilityBlocks { get; set; } 29 | 30 | public Rewards() 31 | { 32 | Inventory = Array.Empty(); 33 | Buildplates = Array.Empty(); 34 | Challenges = Array.Empty(); 35 | PersonaItems = Array.Empty(); 36 | UtilityBlocks = Array.Empty(); 37 | } 38 | } 39 | 40 | public class RewardComponent 41 | { 42 | [JsonProperty("id")] 43 | public Guid Id { get; set; } 44 | 45 | [JsonProperty("amount")] 46 | public int Amount { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Token.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace ProjectEarthServerAPI.Models 4 | { 5 | public class Token 6 | { 7 | public string lifetime { get; set; } 8 | public string clientType { get; set; } 9 | public Dictionary clientProperties { get; set; } // Needs to be optional, so Dictionary instead of fixed class 10 | public Rewards rewards { get; set; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Models/Updates.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | namespace ProjectEarthServerAPI.Models 8 | { 9 | public class Updates 10 | { 11 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 12 | public uint characterProfile { get; set; } 13 | 14 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 15 | public uint crafting { get; set; } 16 | 17 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 18 | public uint inventory { get; set; } 19 | 20 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 21 | public uint playerJournal { get; set; } 22 | 23 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 24 | public uint smelting { get; set; } 25 | 26 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 27 | public uint tokens { get; set; } 28 | 29 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 30 | public uint challenges { get; set; } 31 | 32 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 33 | public uint boosts { get; set; } 34 | 35 | [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] 36 | public uint buildplates { get; set; } 37 | } 38 | 39 | public class UpdateResponse 40 | { 41 | public Updates updates { get; set; } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Hosting; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.Hosting; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.ComponentModel; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using ProjectEarthServerAPI.Models; 11 | using ProjectEarthServerAPI.Util; 12 | using ProjectEarthServerAPI.Models.Features; 13 | using ProjectEarthServerAPI.Models.Player; 14 | using Serilog; 15 | using Serilog.Core; 16 | using Uma.Uuid; 17 | 18 | namespace ProjectEarthServerAPI 19 | { 20 | 21 | public class Program 22 | { 23 | 24 | public static void Main(string[] args) 25 | { 26 | TypeDescriptor.AddAttributes(typeof(Uuid), new TypeConverterAttribute(typeof(StringToUuidConv))); 27 | 28 | // Init Logging 29 | var log = new LoggerConfiguration() 30 | .WriteTo.Console() 31 | .WriteTo.File("logs/debug.txt", rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 8338607, outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}") 32 | .MinimumLevel.Debug() 33 | .CreateLogger(); 34 | 35 | Log.Logger = log; 36 | 37 | //Initialize state singleton from config 38 | StateSingleton.Instance.config = ServerConfig.getFromFile(); 39 | StateSingleton.Instance.catalog = CatalogResponse.FromFiles(StateSingleton.Instance.config.itemsFolderLocation, StateSingleton.Instance.config.efficiencyCategoriesFolderLocation); 40 | StateSingleton.Instance.recipes = Recipes.FromFile(StateSingleton.Instance.config.recipesFileLocation); 41 | StateSingleton.Instance.settings = SettingsResponse.FromFile(StateSingleton.Instance.config.settingsFileLocation); 42 | StateSingleton.Instance.seasonChallenges = ChallengesResponse.FromFile(StateSingleton.Instance.config.seasonChallengesFileLocation); 43 | StateSingleton.Instance.productCatalog = ProductCatalogResponse.FromFile(StateSingleton.Instance.config.productCatalogFileLocation); 44 | StateSingleton.Instance.tappableData = TappableUtils.loadAllTappableSets(); 45 | StateSingleton.Instance.activeTappableTypes = new Dictionary(); 46 | //Start api 47 | CreateHostBuilder(args).Build().Run(); 48 | 49 | Log.Information("Server started!"); 50 | } 51 | 52 | public static IHostBuilder CreateHostBuilder(string[] args) => 53 | Host.CreateDefaultBuilder(args) 54 | //.UseSerilog() 55 | .ConfigureWebHostDefaults(webBuilder => 56 | { 57 | webBuilder.UseStartup(); 58 | }); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/ProjectEarthServerAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net5.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iis": { 6 | "applicationUrl": "http://localhost:80", 7 | "sslPort": 0 8 | }, 9 | "iisExpress": { 10 | "applicationUrl": "http://localhost:80", 11 | "sslPort": 0 12 | } 13 | }, 14 | "$schema": "http://json.schemastore.org/launchsettings.json", 15 | "profiles": { 16 | "IIS Express": { 17 | "commandName": "IISExpress", 18 | "launchUrl": "swagger", 19 | "environmentVariables": { 20 | "ASPNETCORE_ENVIRONMENT": "Development" 21 | } 22 | }, 23 | "ProjectEarthServerAPI": { 24 | "commandName": "Project", 25 | "launchUrl": "swagger", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | }, 29 | "dotnetRunMessages": "true", 30 | "applicationUrl": "http://localhost:80" 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.HttpsPolicy; 4 | using Microsoft.AspNetCore.Mvc; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | using Microsoft.OpenApi.Models; 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Threading.Tasks; 14 | using Microsoft.AspNetCore.Http; 15 | using Microsoft.AspNetCore.ResponseCompression; 16 | using ProjectEarthServerAPI.Util; 17 | using Microsoft.AspNetCore.Authentication; 18 | using ProjectEarthServerAPI.Authentication; 19 | using Serilog; 20 | 21 | namespace ProjectEarthServerAPI 22 | { 23 | public class Startup 24 | { 25 | public Startup(IConfiguration configuration) 26 | { 27 | Configuration = configuration; 28 | } 29 | 30 | public IConfiguration Configuration { get; } 31 | 32 | // This method gets called by the runtime. Use this method to add services to the container. 33 | public void ConfigureServices(IServiceCollection services) 34 | { 35 | services.AddControllers(); 36 | 37 | services.AddResponseCompression(options => 38 | { 39 | options.Providers.Add(); 40 | }); 41 | 42 | services.AddResponseCaching(); 43 | 44 | services.AddApiVersioning(config => 45 | { 46 | config.DefaultApiVersion = new ApiVersion(1, 1); 47 | config.AssumeDefaultVersionWhenUnspecified = true; 48 | config.ReportApiVersions = true; 49 | }); 50 | 51 | services.AddAuthentication("GenoaAuth") 52 | .AddScheme("GenoaAuth", null); 53 | } 54 | 55 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 56 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 57 | { 58 | if (env.IsDevelopment()) 59 | { 60 | //app.UseDeveloperExceptionPage(); 61 | } 62 | 63 | //app.UseSerilogRequestLogging(); 64 | 65 | app.UseETagger(); 66 | //app.UseHttpsRedirection(); 67 | 68 | app.UseRouting(); 69 | 70 | app.UseAuthentication(); 71 | app.UseAuthorization(); 72 | 73 | app.UseWebSockets(); 74 | 75 | app.UseResponseCaching(); 76 | 77 | app.UseResponseCompression(); 78 | 79 | //app.UseSession(); 80 | 81 | app.UseEndpoints(endpoints => 82 | { 83 | endpoints.MapControllers(); 84 | }); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/AdventureUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using ProjectEarthServerAPI.Models; 5 | using ProjectEarthServerAPI.Models.Features; 6 | using ProjectEarthServerAPI.Models.Multiplayer; 7 | using ProjectEarthServerAPI.Models.Multiplayer.Adventure; 8 | 9 | namespace ProjectEarthServerAPI.Util 10 | { 11 | public class AdventureUtils 12 | { 13 | public Dictionary crystalRarityList = StateSingleton.Instance.catalog.result.items 14 | .FindAll(select => select.item.type == "AdventureScroll") 15 | .ToDictionary(pred => pred.id, pred => pred.rarity); 16 | 17 | public static AdventureRequestResult RedeemCrystal(string playerId, PlayerAdventureRequest adventureRequest, Guid crystalId) 18 | { 19 | InventoryUtils.RemoveItemFromInv(playerId, crystalId); 20 | 21 | var selectedAdventureIcon = "genoa:adventure_generic_map_b"; 22 | var selectedAdventureId = "b7335819-c123-49b9-83fb-8a0ec5032779"; 23 | 24 | var adventureLocation = new LocationResponse.ActiveLocation 25 | { 26 | coordinate = adventureRequest.coordinate, 27 | encounterMetadata = new EncounterMetadata 28 | { 29 | anchorId = "", 30 | anchorState = "Off", 31 | augmentedImageSetId = "", 32 | encounterType = EncounterType.None, 33 | locationId = Guid.Empty, 34 | worldId = Guid.NewGuid() // TODO: Replace this with actual adventure id 35 | }, 36 | expirationTime = DateTime.UtcNow.Add(TimeSpan.FromMinutes(10.00)), 37 | spawnTime = DateTime.UtcNow, 38 | icon = selectedAdventureIcon, 39 | id = selectedAdventureId, 40 | metadata = new(), 41 | tileId = string.Join("-", 42 | Tile.getTileForCords(adventureRequest.coordinate.latitude, adventureRequest.coordinate.longitude)), 43 | type = "PlayerAdventure" 44 | }; 45 | 46 | return new AdventureRequestResult {result = adventureLocation, updates = new Updates()}; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/BoostUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Security.Cryptography; 6 | using System.Text.RegularExpressions; 7 | using Newtonsoft.Json; 8 | using ProjectEarthServerAPI.Models; 9 | using ProjectEarthServerAPI.Models.Features; 10 | 11 | namespace ProjectEarthServerAPI.Util 12 | { 13 | public class BoostUtils 14 | { 15 | private static CatalogResponse catalog = StateSingleton.Instance.catalog; 16 | 17 | public static BoostResponse ReadBoosts(string playerId) 18 | { 19 | var resp = GenericUtils.ParseJsonFile(playerId, "boosts"); 20 | if (resp.result.miniFigs.Count != 5) 21 | { 22 | resp.result.miniFigs = new List(new object[5]); 23 | WriteBoosts(playerId, resp); 24 | } 25 | 26 | if (resp.result.potions.Count != 5) 27 | { 28 | resp.result.potions = new List(new Potion[5]); 29 | WriteBoosts(playerId, resp); 30 | } 31 | 32 | return resp; 33 | } 34 | 35 | public static bool WriteBoosts(string playerId, BoostResponse resp) 36 | { 37 | return GenericUtils.WriteJsonFile(playerId, resp, "boosts"); 38 | } 39 | 40 | public static CraftingUpdates ActivateBoost(string playerId, Guid boostId) 41 | { 42 | var updates = new CraftingUpdates {updates = new Updates()}; 43 | 44 | List expirationTimes = new List(); // This looks bad (and it totally is), but we can optimize it at a later date 45 | var currentTime = DateTime.UtcNow; 46 | 47 | var baseBoosts = ReadBoosts(playerId); 48 | var boostToApply = catalog.result.items.Find(match => match.id == boostId); 49 | 50 | 51 | var indexNum = 0; 52 | if (baseBoosts.result.scenarioBoosts.death != null) 53 | indexNum = baseBoosts.result.scenarioBoosts.death.Count; // Only used if boost is scenario specific 54 | 55 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 56 | 57 | var locOfEffect = baseBoosts.result.activeEffects.Find(match => match.effect == boostToApply.item.boostMetadata.effects[0]); // null when not an active effect, e.g scenario boost 58 | 59 | ActiveBoost locOfBoost = null; 60 | 61 | 62 | if (baseBoosts.result.scenarioBoosts.death != null) 63 | { 64 | locOfBoost = baseBoosts.result.scenarioBoosts.death.Find(match => 65 | match.effects.Any(pred => pred == boostToApply.item.boostMetadata.effects[0])); 66 | } 67 | 68 | if (locOfEffect != null && boostToApply.item.boostMetadata.additive && boostToApply.item.boostMetadata.scenario == null) 69 | { 70 | locOfEffect.effect.duration += boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay; 71 | baseBoosts.result.potions.Find(match => match.itemId == boostId).expiration += 72 | boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay; 73 | } 74 | else if (locOfBoost != null && boostToApply.item.boostMetadata.additive && boostToApply.item.boostMetadata.scenario == "Death") 75 | { 76 | indexNum = baseBoosts.result.scenarioBoosts.death.IndexOf(locOfBoost); 77 | locOfBoost.expiration += boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay; 78 | baseBoosts.result.potions.Find(match => match.itemId == boostId).expiration += boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay; 79 | } 80 | else 81 | { 82 | Dictionary uuidDict = new Dictionary(); 83 | if (boostToApply.item.boostMetadata.scenario == null) 84 | { 85 | foreach (Item.Effect effect in boostToApply.item.boostMetadata.effects) 86 | { 87 | baseBoosts.result.activeEffects.Add(new ActiveEffect {effect = effect, expiration = currentTime.Add(effect.duration.Value)}); 88 | } 89 | } 90 | else 91 | { 92 | baseBoosts.result.scenarioBoosts.death ??= new List(); 93 | 94 | baseBoosts.result.scenarioBoosts.death.Add(new ActiveBoost {effects = new List(), enabled = true, expiration = currentTime.Add(boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay), instanceId = Guid.NewGuid().ToString()}); 95 | 96 | foreach (Item.Effect effect in boostToApply.item.boostMetadata.effects) 97 | { 98 | baseBoosts.result.scenarioBoosts.death[indexNum].effects.Add(effect); 99 | } 100 | 101 | uuidDict.Add(boostId, baseBoosts.result.scenarioBoosts.death[indexNum].instanceId); 102 | } 103 | 104 | Potion potion = null; 105 | 106 | if (boostToApply.item.boostMetadata.activeDuration != null) 107 | { 108 | potion = new Potion {enabled = true, expiration = currentTime.Add(boostToApply.item.boostMetadata.activeDuration.Value.TimeOfDay), instanceId = Guid.NewGuid().ToString(), itemId = boostId}; 109 | } 110 | else if (boostToApply.item.boostMetadata.effects[0].duration != null) 111 | { 112 | potion = new Potion {enabled = true, expiration = currentTime.Add(boostToApply.item.boostMetadata.effects[0].duration.Value), instanceId = Guid.NewGuid().ToString(), itemId = boostId}; 113 | } 114 | 115 | if (uuidDict.ContainsKey(boostId) && potion != null) 116 | { 117 | potion.instanceId = uuidDict[boostId]; 118 | } 119 | 120 | var nullPotionIndex = baseBoosts.result.potions.FindIndex(match => match == null); 121 | if (nullPotionIndex != -1) 122 | baseBoosts.result.potions[nullPotionIndex] = potion; 123 | } 124 | 125 | 126 | if (boostToApply.item.boostMetadata.canBeRemoved) 127 | { 128 | InventoryUtils.RemoveItemFromInv(playerId, boostId); // UNCOMMENT/COMMENT THIS LINE TO REMOVE BOOSTS FROM INVENTORY WHEN USED 129 | updates.updates.inventory = nextStreamId; 130 | } 131 | 132 | foreach (ActiveEffect effect in baseBoosts.result.activeEffects) 133 | { 134 | expirationTimes.Add(effect.expiration.Value); 135 | } 136 | 137 | if (baseBoosts.result.scenarioBoosts.death != null) 138 | foreach (ActiveBoost boost in baseBoosts.result.scenarioBoosts.death) 139 | { 140 | expirationTimes.Add(boost.expiration.Value); 141 | } 142 | 143 | baseBoosts.result.expiration = expirationTimes.Min(); 144 | 145 | baseBoosts.result.statusEffects = CalculateStatusEffects(baseBoosts); 146 | 147 | WriteBoosts(playerId, baseBoosts); 148 | updates.updates.boosts = nextStreamId; 149 | return updates; 150 | } 151 | 152 | public static BoostResponse UpdateBoosts(string playerId) 153 | { 154 | List expirationTimes = new List(); // This looks bad (and it totally is), but we can optimize it at a later date 155 | var baseBoosts = ReadBoosts(playerId); 156 | var currentTime = DateTime.UtcNow; 157 | 158 | baseBoosts.result.activeEffects.RemoveAll(match => match.expiration < currentTime); 159 | 160 | baseBoosts.result.scenarioBoosts.death?.RemoveAll(match => match.expiration < currentTime); 161 | 162 | baseBoosts.result.potions.RemoveAll(match => match?.expiration < currentTime); 163 | 164 | foreach (ActiveEffect effect in baseBoosts.result.activeEffects) 165 | { 166 | expirationTimes.Add(effect.expiration.Value); 167 | } 168 | 169 | if (baseBoosts.result.scenarioBoosts.death != null) 170 | foreach (ActiveBoost boost in baseBoosts.result.scenarioBoosts.death) 171 | { 172 | expirationTimes.Add(boost.expiration.Value); 173 | } 174 | 175 | try 176 | { 177 | baseBoosts.result.expiration = expirationTimes.Min(); 178 | } 179 | catch 180 | { 181 | baseBoosts.result.expiration = null; 182 | } 183 | 184 | baseBoosts.result.statusEffects = CalculateStatusEffects(baseBoosts); 185 | 186 | 187 | WriteBoosts(playerId, baseBoosts); 188 | 189 | return baseBoosts; 190 | } 191 | 192 | public static CraftingUpdates RemoveBoost(string playerId, string boostInstanceId) 193 | { 194 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 195 | var baseBoosts = ReadBoosts(playerId); 196 | var potionToRemove = baseBoosts.result.potions.Find(match => match.instanceId == boostInstanceId); 197 | var boostToRemove = catalog.result.items.Find(match => match.id == potionToRemove.itemId); 198 | 199 | baseBoosts.result.scenarioBoosts.death?.RemoveAll(match => match.instanceId == boostInstanceId); 200 | 201 | baseBoosts.result.potions.Remove(potionToRemove); 202 | baseBoosts.result.activeEffects.RemoveAll(match => 203 | boostToRemove.item.boostMetadata.effects.Contains(match.effect) && match.expiration == potionToRemove.expiration); 204 | 205 | baseBoosts.result.statusEffects = CalculateStatusEffects(baseBoosts); 206 | 207 | WriteBoosts(playerId, baseBoosts); 208 | 209 | var updates = new CraftingUpdates {updates = new Updates()}; 210 | 211 | updates.updates.boosts = nextStreamId; 212 | 213 | return updates; 214 | } 215 | 216 | public static StatusEffects CalculateStatusEffects(BoostResponse boosts) // TODO: Which values does it use and need? More research on official servers needed 217 | { 218 | var baseBoosts = boosts; 219 | var statusEffects = new StatusEffects(); 220 | var baseSettings = StateSingleton.Instance.settings; 221 | 222 | statusEffects.tappableInteractionRadius = 223 | (int?)(baseSettings.result.tappableinteractionradius + baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "tappableinteractionradius")?.effect.value); 224 | 225 | //baseBoosts.result.statusEffects.itemExperiencePointRates = baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "tappableinteractionradius").effect.value); 226 | statusEffects.attackDamageRate = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "tappableinteractionradius")?.effect.value; 227 | statusEffects.blockDamageRate = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "blockdamage")?.effect.value; 228 | statusEffects.craftingSpeed = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "craftingspeed")?.effect.value; 229 | statusEffects.experiencePointRate = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "itemexperiencepoints")?.effect.value; // Find out if this or the other one 230 | statusEffects.foodHealthRate = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "foodhealth")?.effect.value; 231 | statusEffects.maximumPlayerHealth = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "maximumplayerhealth")?.effect.value; 232 | statusEffects.playerDefenseRate = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "playerdefense")?.effect.value; 233 | statusEffects.smeltingFuelIntensity = (int?)baseBoosts.result.activeEffects.Find(match => match.effect.type.ToLower() == "smeltingfuelintensity")?.effect.value; 234 | 235 | return statusEffects; 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/BuildplateUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | using ProjectEarthServerAPI.Models.Buildplate; 6 | using ProjectEarthServerAPI.Models.Multiplayer; 7 | using Serilog; 8 | 9 | namespace ProjectEarthServerAPI.Util 10 | { 11 | public class BuildplateUtils 12 | { 13 | public static BuildplateListResponse GetBuildplatesList(string playerId) 14 | { 15 | var buildplates = ReadPlayerBuildplateList(playerId); 16 | BuildplateListResponse list = new BuildplateListResponse {result = new List()}; 17 | foreach (Guid id in buildplates.UnlockedBuildplates) 18 | { 19 | var bp = ReadBuildplate(id); 20 | bp.order = buildplates.UnlockedBuildplates.IndexOf(id); 21 | list.result.Add(bp.id != bp.templateId ? ReadBuildplate(id) : CloneTemplateBuildplate(playerId, bp)); 22 | } 23 | 24 | buildplates.LockedBuildplates.ForEach(action => 25 | { 26 | var bp = ReadBuildplate(action); 27 | bp.order = buildplates.LockedBuildplates.IndexOf(action) + buildplates.UnlockedBuildplates.Count; 28 | list.result.Add(bp); 29 | }); 30 | 31 | return list; 32 | } 33 | 34 | public static PlayerBuildplateList ReadPlayerBuildplateList(string playerId) 35 | => GenericUtils.ParseJsonFile(playerId, "buildplates"); 36 | 37 | public static void WritePlayerBuildplateList(string playerId, PlayerBuildplateList list) 38 | => GenericUtils.WriteJsonFile(playerId, list, "buildplates"); 39 | 40 | public static void AddToPlayer(string playerId, Guid buildplateId) 41 | { 42 | var bpList = ReadPlayerBuildplateList(playerId); 43 | 44 | if (!bpList.UnlockedBuildplates.Contains(buildplateId)) 45 | bpList.UnlockedBuildplates.Add(buildplateId); 46 | } 47 | 48 | public static BuildplateData CloneTemplateBuildplate(string playerId, BuildplateData templateBuildplate) 49 | { 50 | var clonedId = Guid.NewGuid(); 51 | BuildplateData clonedBuildplate = templateBuildplate; 52 | clonedBuildplate.id = clonedId; 53 | clonedBuildplate.locked = false; 54 | 55 | WriteBuildplate(clonedBuildplate); 56 | 57 | var list = ReadPlayerBuildplateList(playerId); 58 | var index = list.UnlockedBuildplates.IndexOf(templateBuildplate.id); 59 | list.UnlockedBuildplates.Remove(templateBuildplate.id); 60 | list.UnlockedBuildplates.Insert(index, clonedId); 61 | 62 | WritePlayerBuildplateList(playerId, list); 63 | 64 | return clonedBuildplate; 65 | } 66 | 67 | public static BuildplateShareResponse GetBuildplateById(BuildplateRequest buildplateReq) 68 | { 69 | BuildplateData buildplate = ReadBuildplate(buildplateReq.buildplateId); 70 | 71 | return new BuildplateShareResponse {result = new BuildplateShareResponse.BuildplateShareInfo {buildplateData = buildplate, playerId = null}}; 72 | } 73 | 74 | public static void UpdateBuildplateAndList(BuildplateShareResponse data, string playerId) 75 | { 76 | data.result.buildplateData.eTag ??= "\"0xAAAAAAAAAAAAAAA\""; // TODO: If we ever use eTags for buildplates, replace this 77 | WriteBuildplate(data); 78 | 79 | var list = ReadPlayerBuildplateList(playerId); 80 | PlayerBuildplateList newList = new PlayerBuildplateList(); 81 | for (int i = list.UnlockedBuildplates.IndexOf(data.result.buildplateData.id); i > 0; i--) 82 | { 83 | list.UnlockedBuildplates[i] = list.UnlockedBuildplates[i - 1]; 84 | } 85 | 86 | list.UnlockedBuildplates[0] = data.result.buildplateData.id; 87 | 88 | WritePlayerBuildplateList(playerId, list); 89 | } 90 | 91 | public static BuildplateData ReadBuildplate(Guid buildplateId) 92 | { 93 | var filepath = $"./data/buildplates/{buildplateId}.json"; // TODO: Add to config 94 | if (!File.Exists(filepath)) 95 | { 96 | Log.Error($"Error: Tried to read buildplate that does not exist! BuildplateID: {buildplateId}"); 97 | return null; 98 | } 99 | 100 | var buildplateJson = File.ReadAllText(filepath); 101 | var parsedobj = JsonConvert.DeserializeObject(buildplateJson); 102 | return parsedobj; 103 | } 104 | 105 | public static void WriteBuildplate(BuildplateData data) 106 | { 107 | var buildplateId = data.id; 108 | var filepath = $"./data/buildplates/{buildplateId}.json"; // TODO: Add to config 109 | 110 | data.lastUpdated = DateTime.UtcNow; 111 | 112 | File.WriteAllText(filepath, JsonConvert.SerializeObject(data)); 113 | } 114 | 115 | public static void WriteBuildplate(BuildplateShareResponse shareResponse) 116 | => WriteBuildplate(shareResponse.result.buildplateData); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/ChallengeUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Linq; 5 | using Microsoft.OpenApi.Extensions; 6 | using ProjectEarthServerAPI.Models; 7 | using ProjectEarthServerAPI.Models.Player; 8 | using Serilog; 9 | 10 | namespace ProjectEarthServerAPI.Util 11 | { 12 | public class ChallengeUtils 13 | { 14 | private static readonly ChallengesList ChallengeList = StateSingleton.Instance.seasonChallenges.result; 15 | 16 | public static bool ActivateChallengeForPlayer(string playerId, Guid challengeId) 17 | { 18 | var challenge = ChallengeList.challenges.First(pred => pred.Key == challengeId).Value; 19 | var playerChallenges = ReadChallenges(playerId); 20 | bool shouldBeActivated = false; 21 | 22 | foreach (KeyValuePair prereqChallenge in playerChallenges.result.challenges.Where(pred => 23 | challenge.prerequisiteIds.Contains(pred.Key))) 24 | { 25 | if (!shouldBeActivated) 26 | { 27 | switch (challenge.prerequisiteLogicalCondition) 28 | { 29 | case ChallengeLogicCondition.And: 30 | if (!prereqChallenge.Value.isComplete) 31 | return false; 32 | break; 33 | 34 | case ChallengeLogicCondition.Or: 35 | if (prereqChallenge.Value.isComplete) 36 | shouldBeActivated = true; 37 | break; 38 | } 39 | } 40 | else break; 41 | } 42 | 43 | if (challenge.duration == ChallengeDuration.Season) 44 | playerChallenges.result.activeSeasonChallenge = challengeId; 45 | 46 | playerChallenges.result.challenges[challengeId].state = ChallengeState.Active; 47 | 48 | Log.Information($"[{playerId}]: Activating challenge {challengeId}!"); 49 | WriteChallenges(playerId, playerChallenges); 50 | 51 | return true; 52 | } 53 | 54 | public static Updates RedeemChallengeForPlayer(string playerId, Guid challengeId) 55 | { 56 | var challenge = ChallengeList.challenges.First(pred => pred.Key == challengeId).Value; 57 | var playerChallenges = ReadChallenges(playerId); 58 | 59 | playerChallenges.result.challenges[challengeId].isComplete = true; 60 | playerChallenges.result.challenges[challengeId].state = ChallengeState.Completed; 61 | playerChallenges.result.challenges[challengeId].percentComplete = 100; 62 | 63 | 64 | WriteChallenges(playerId, playerChallenges); 65 | 66 | var completionToken = new Token {clientProperties = new Dictionary(), clientType = "challenge.completed", lifetime = "Persistent", rewards = challenge.rewards}; 67 | completionToken.clientProperties.Add("challengeid", challengeId.ToString()); 68 | completionToken.clientProperties.Add("category", challenge.category.GetDisplayName()); 69 | completionToken.clientProperties.Add("expirationtimeutc", playerChallenges.result.challenges[challengeId].endTimeUtc.Value.ToString(CultureInfo.InvariantCulture)); 70 | 71 | var returnUpdates = RewardUtils.RedeemRewards(playerId, challenge.rewards); 72 | if (TokenUtils.AddToken(playerId, completionToken)) 73 | returnUpdates.tokens = 1; // Not the actual stream id ofc, but we just need to tell the game to reload the tokens 74 | 75 | return returnUpdates; 76 | } 77 | 78 | public static Updates ProgressChallenge(string playerId, ChallengeEventType challengeEvent, Guid eventId, int amount = 1) 79 | { 80 | var playerChallenges = ReadChallenges(playerId); 81 | var activeChallenges = 82 | playerChallenges.result.challenges.Where(pred => pred.Value.state == ChallengeState.Active).ToDictionary(pred => pred.Key, pred => pred.Value); 83 | 84 | // TODO: Implement challenge backend, since challenge requirements are not set in the response 85 | return new Updates() {challenges = GenericUtils.GetNextStreamVersion()}; 86 | } 87 | 88 | public static ChallengesResponse ReloadChallenges(string playerId) 89 | { 90 | var playerChallenges = ReadChallenges(playerId); 91 | return playerChallenges; 92 | } 93 | 94 | private static ChallengesResponse ReadChallenges(string playerId) 95 | { 96 | return GenericUtils.ParseJsonFile(playerId, "challenges"); 97 | } 98 | 99 | private static bool WriteChallenges(string playerId, ChallengesResponse challenges) 100 | { 101 | return GenericUtils.WriteJsonFile(playerId, challenges, "challenges"); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/CraftingUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json.Linq; 5 | using ProjectEarthServerAPI.Models; 6 | using ProjectEarthServerAPI.Models.Features; 7 | using Serilog; 8 | 9 | namespace ProjectEarthServerAPI.Util 10 | { 11 | public class CraftingUtils // TODO: Bug Fix: Cancelling a crafting task that you already collected some results 12 | // TODO: for returns the entire crafting cost, not just the remaining part 13 | { 14 | private static Recipes recipeList = StateSingleton.Instance.recipes; 15 | private static Dictionary> craftingJobs = new Dictionary>(); 16 | 17 | public static bool StartCraftingJob(string playerId, int slot, CraftingRequest request) // TODO: Check if slot not unlocked (not a big priority) 18 | { 19 | recipeList ??= Recipes.FromFile("./data/recipes"); 20 | 21 | var recipe = recipeList.result.crafting.Find(match => match.id == request.RecipeId); 22 | 23 | if (recipe != null) 24 | { 25 | var itemsToReturn = recipe.returnItems.ToList(); 26 | 27 | foreach (RecipeIngredients ingredient in recipe.ingredients) 28 | { 29 | if (itemsToReturn.Find(match => 30 | match.id == ingredient.items[0] && match.amount == ingredient.quantity) == null) 31 | { 32 | InventoryUtils.RemoveItemFromInv(playerId, ingredient.items[0], ingredient.quantity * request.Multiplier); 33 | } 34 | } 35 | 36 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 37 | 38 | CraftingSlotInfo job = new CraftingSlotInfo 39 | { 40 | available = 0, 41 | boostState = null, 42 | completed = 0, 43 | escrow = request.Ingredients, 44 | nextCompletionUtc = null, 45 | output = recipe.output, 46 | recipeId = recipe.id, 47 | sessionId = request.SessionId, 48 | state = "Active", 49 | streamVersion = nextStreamId, 50 | total = request.Multiplier, 51 | totalCompletionUtc = DateTime.UtcNow.Add(recipe.duration.TimeOfDay * request.Multiplier), 52 | unlockPrice = null 53 | }; 54 | 55 | if (request.Multiplier != 1) 56 | { 57 | job.nextCompletionUtc = DateTime.UtcNow.Add(recipe.duration.TimeOfDay); 58 | } 59 | 60 | if (!craftingJobs.ContainsKey(playerId)) 61 | { 62 | craftingJobs.Add(playerId, new Dictionary()); 63 | craftingJobs[playerId].Add(1, new CraftingSlotInfo()); 64 | craftingJobs[playerId].Add(2, new CraftingSlotInfo()); 65 | craftingJobs[playerId].Add(3, new CraftingSlotInfo()); 66 | } 67 | 68 | craftingJobs[playerId][slot] = job; 69 | 70 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 71 | 72 | Log.Debug($"[{playerId}]: Initiated crafting job in slot {slot}."); 73 | 74 | return true; 75 | } 76 | 77 | return false; 78 | } 79 | 80 | public static CraftingSlotResponse GetCraftingJobInfo(string playerId, int slot) 81 | { 82 | try 83 | { 84 | var job = craftingJobs[playerId][slot]; 85 | var recipe = recipeList.result.crafting.Find(match => match.id == job.recipeId & !match.deprecated); 86 | var updates = new Updates(); 87 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 88 | 89 | job.streamVersion = nextStreamId; 90 | 91 | if (job.totalCompletionUtc != null && DateTime.Compare(job.totalCompletionUtc.Value, DateTime.UtcNow) < 0 && job.recipeId != null) 92 | { 93 | job.available = job.total - job.completed; 94 | job.completed += job.available; 95 | job.nextCompletionUtc = null; 96 | job.state = "Completed"; 97 | job.escrow = new InputItem[0]; 98 | } 99 | /*else 100 | { 101 | 102 | job.available++; 103 | //job.completed++; 104 | job.state = "Available"; 105 | job.streamVersion = nextStreamId; 106 | job.nextCompletionUtc = job.nextCompletionUtc.Value.Add(recipe.duration.TimeOfDay); 107 | 108 | for (int i = 0; i < job.escrow.Length - 1; i++) 109 | { 110 | job.escrow[i].quantity -= recipe.ingredients[i].quantity; 111 | } 112 | 113 | }*/ 114 | 115 | updates.crafting = nextStreamId; 116 | 117 | var returnResponse = new CraftingSlotResponse {result = job, updates = updates}; 118 | 119 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 120 | 121 | Log.Debug($"[{playerId}]: Requested crafting slot {slot} status."); 122 | 123 | return returnResponse; 124 | } 125 | catch (Exception e) 126 | { 127 | Log.Error($"[{playerId}]: Error while getting crafting job info! Crafting Slot: {slot}"); 128 | Log.Debug($"Exception: {e.StackTrace}"); 129 | return null; 130 | } 131 | } 132 | 133 | public static CollectItemsResponse FinishCraftingJob(string playerId, int slot) 134 | { 135 | var job = craftingJobs[playerId][slot]; 136 | var recipe = recipeList.result.crafting.Find(match => match.id == job.recipeId & !match.deprecated); 137 | int craftedAmount = 0; 138 | 139 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 140 | 141 | var returnResponse = new CollectItemsResponse {result = new CollectItemsInfo {rewards = new Rewards(),}, updates = new Updates()}; 142 | 143 | if (job.completed != job.total && job.nextCompletionUtc != null) 144 | { 145 | if (DateTime.UtcNow >= job.nextCompletionUtc) 146 | { 147 | craftedAmount++; 148 | while (DateTime.UtcNow >= job.nextCompletionUtc && job.nextCompletionUtc.Value.Add(recipe.duration.TimeOfDay) < job.totalCompletionUtc && craftedAmount < job.total - job.completed) 149 | { 150 | job.nextCompletionUtc = job.nextCompletionUtc.Value.Add(recipe.duration.TimeOfDay); 151 | craftedAmount++; 152 | } 153 | 154 | job.nextCompletionUtc = job.nextCompletionUtc.Value.Add(recipe.duration.TimeOfDay); 155 | job.completed += craftedAmount; 156 | //job.available -= craftedAmount; 157 | for (int i = 0; i < job.escrow.Length - 1; i++) 158 | { 159 | job.escrow[i].quantity -= recipe.ingredients[i].quantity * craftedAmount; 160 | } 161 | 162 | job.streamVersion = nextStreamId; 163 | 164 | InventoryUtils.AddItemToInv(playerId, job.output.itemId, job.output.quantity * craftedAmount); 165 | } 166 | } 167 | else 168 | { 169 | craftedAmount = job.available; 170 | InventoryUtils.AddItemToInv(playerId, job.output.itemId, job.output.quantity * craftedAmount); 171 | // TODO: Add to challenges, tokens, journal (when implemented) 172 | } 173 | 174 | if (!TokenUtils.GetTokenResponseForUserId(playerId).Result.tokens.Any(match => match.Value.clientProperties.ContainsKey("itemid") && match.Value.clientProperties["itemid"] == job.output.itemId.ToString())) 175 | { 176 | //TokenUtils.AddItemToken(playerId, job.output.itemId); -> List of item tokens not known. Could cause issues later, for now we just disable it. 177 | returnResponse.updates.tokens = nextStreamId; 178 | } 179 | 180 | returnResponse.result.rewards.Inventory = returnResponse.result.rewards.Inventory.Append(new RewardComponent {Amount = job.output.quantity * craftedAmount, Id = job.output.itemId}).ToArray(); 181 | 182 | returnResponse.updates.crafting = nextStreamId; 183 | returnResponse.updates.inventory = nextStreamId; 184 | returnResponse.updates.playerJournal = nextStreamId; 185 | 186 | 187 | if (job.completed == job.total || job.nextCompletionUtc == null) 188 | { 189 | job.nextCompletionUtc = null; 190 | job.available = 0; 191 | job.completed = 0; 192 | job.recipeId = null; 193 | job.sessionId = null; 194 | job.state = "Empty"; 195 | job.total = 0; 196 | job.boostState = null; 197 | job.totalCompletionUtc = null; 198 | job.unlockPrice = null; 199 | job.output = null; 200 | job.streamVersion = nextStreamId; 201 | } 202 | 203 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 204 | 205 | Log.Debug($"[{playerId}]: Collected results of crafting slot {slot}."); 206 | 207 | return returnResponse; 208 | } 209 | 210 | public static CraftingSlotResponse CancelCraftingJob(string playerId, int slot) 211 | { 212 | var job = craftingJobs[playerId][slot]; 213 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 214 | var resp = new CraftingSlotResponse {result = new CraftingSlotInfo(), updates = new Updates()}; 215 | 216 | foreach (InputItem item in job.escrow) 217 | { 218 | InventoryUtils.AddItemToInv(playerId, item.itemId, item.quantity); 219 | } 220 | 221 | job.nextCompletionUtc = null; 222 | job.available = 0; 223 | job.completed = 0; 224 | job.recipeId = null; 225 | job.sessionId = null; 226 | job.state = "Empty"; 227 | job.total = 0; 228 | job.boostState = null; 229 | job.totalCompletionUtc = null; 230 | job.unlockPrice = null; 231 | job.output = null; 232 | job.streamVersion = nextStreamId; 233 | 234 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 235 | 236 | Log.Debug($"[{playerId}]: Cancelled crafting job in slot {slot}."); 237 | 238 | resp.updates.crafting = nextStreamId; 239 | return resp; 240 | } 241 | 242 | public static CraftingUpdates UnlockCraftingSlot(string playerId, int slot) 243 | { 244 | var job = craftingJobs[playerId][slot]; 245 | 246 | RubyUtils.SetRubies(playerId, job.unlockPrice.cost - job.unlockPrice.discount, false); 247 | job.state = "Empty"; 248 | job.unlockPrice = null; 249 | 250 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 251 | var returnUpdates = new CraftingUpdates {updates = new Updates()}; 252 | 253 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 254 | 255 | Log.Debug($"[{playerId}]: Unlocked crafting slot {slot}."); 256 | 257 | returnUpdates.updates.crafting = nextStreamId; 258 | 259 | return returnUpdates; 260 | } 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/DateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace ProjectEarthServerAPI.Util 6 | { 7 | public class DateTimeConverter : JsonConverter 8 | { 9 | public override DateTime ReadJson(JsonReader reader, Type objectType, [AllowNull] DateTime existingValue, bool hasExistingValue, JsonSerializer serializer) 10 | { 11 | return (DateTime)reader.Value; 12 | } 13 | 14 | public override void WriteJson(JsonWriter writer, [AllowNull] DateTime value, JsonSerializer serializer) 15 | { 16 | writer.WriteValue(value.ToString("yyyy-MM-ddTHH:mm:ssZ")); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/ETag.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Security.Cryptography; 3 | using System.Threading.Tasks; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore.WebUtilities; 7 | using Microsoft.Net.Http.Headers; 8 | 9 | namespace ProjectEarthServerAPI.Util 10 | { 11 | public class ETagMiddleware 12 | { 13 | private readonly RequestDelegate _next; 14 | 15 | public ETagMiddleware(RequestDelegate next) 16 | { 17 | _next = next; 18 | } 19 | 20 | public async Task InvokeAsync(HttpContext context) 21 | { 22 | var response = context.Response; 23 | var originalStream = response.Body; 24 | 25 | using (var ms = new MemoryStream()) 26 | { 27 | response.Body = ms; 28 | 29 | await _next(context); 30 | 31 | if (IsEtagSupported(response)) 32 | { 33 | string checksum = CalculateChecksum(ms); 34 | 35 | response.Headers[HeaderNames.ETag] = checksum; 36 | 37 | if (context.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var etag) && checksum == etag) 38 | { 39 | response.StatusCode = StatusCodes.Status304NotModified; 40 | response.ContentLength = 0L; 41 | response.Headers[HeaderNames.ContentLength] = "0"; 42 | return; 43 | } 44 | } 45 | 46 | ms.Position = 0; 47 | await ms.CopyToAsync(originalStream); 48 | } 49 | } 50 | 51 | private static bool IsEtagSupported(HttpResponse response) 52 | { 53 | if (response.StatusCode != StatusCodes.Status200OK) 54 | return false; 55 | 56 | // The 20kb length limit is not based in science. Feel free to change 57 | if (response.Body.Length > 20 * 1024) 58 | return false; 59 | 60 | if (response.Headers.ContainsKey(HeaderNames.ETag)) 61 | return false; 62 | 63 | return true; 64 | } 65 | 66 | private static string CalculateChecksum(MemoryStream ms) 67 | { 68 | string checksum = ""; 69 | 70 | using (var algo = SHA1.Create()) 71 | { 72 | ms.Position = 0; 73 | byte[] bytes = algo.ComputeHash(ms); 74 | checksum = $"\"{WebEncoders.Base64UrlEncode(bytes)}\""; 75 | } 76 | 77 | return checksum; 78 | } 79 | } 80 | 81 | public static class ApplicationBuilderExtensions 82 | { 83 | public static void UseETagger(this IApplicationBuilder app) 84 | { 85 | app.UseMiddleware(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/GenericUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Newtonsoft.Json; 4 | using ProjectEarthServerAPI.Models.Player; 5 | using Serilog; 6 | 7 | namespace ProjectEarthServerAPI.Util 8 | { 9 | public class GenericUtils 10 | { 11 | private static uint _streamVersion = 0; 12 | 13 | public static T ParseJsonFile(string playerId, string fileNameWithoutJsonExtension) where T : new() 14 | { 15 | var filepath = $"./data/players/{playerId}/{fileNameWithoutJsonExtension}.json"; 16 | if (!File.Exists(filepath)) 17 | { 18 | if (!Directory.Exists($"./data/players/{playerId}")) 19 | Directory.CreateDirectory($"./data/players/{playerId}"); 20 | 21 | SetupJsonFile(playerId, filepath); // Generic setup for each player specific json type 22 | } 23 | 24 | var invjson = File.ReadAllText(filepath); 25 | var parsedobj = JsonConvert.DeserializeObject(invjson); 26 | return parsedobj; 27 | } 28 | 29 | private static bool SetupJsonFile(string playerId, string filepath) where T : new() 30 | { 31 | try 32 | { 33 | Log.Information($"[{playerId}]: Creating default json with Type: {typeof(T)}."); 34 | var obj = new T(); // TODO: Implement Default Values for each player property/json we store for them 35 | 36 | File.WriteAllText(filepath, JsonConvert.SerializeObject(obj)); 37 | return true; 38 | } 39 | catch (Exception ex) 40 | { 41 | Log.Error($"[{playerId}]: Creating default json failed! Type: {typeof(T)}"); 42 | Log.Debug($"Exception: {ex}"); 43 | return false; 44 | } 45 | } 46 | 47 | public static bool WriteJsonFile(string playerId, T objToWrite, string fileNameWithoutJsonExtension) 48 | { 49 | try 50 | { 51 | var filepath = $"./data/players/{playerId}/{fileNameWithoutJsonExtension}.json"; // Path should exist, as you cant really write to the file before reading it first 52 | 53 | File.WriteAllText(filepath, JsonConvert.SerializeObject(objToWrite)); 54 | 55 | return true; 56 | } 57 | catch 58 | { 59 | return false; 60 | } 61 | } 62 | 63 | public static uint GetNextStreamVersion() 64 | { 65 | _streamVersion++; 66 | return _streamVersion; 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/JournalUtils.cs: -------------------------------------------------------------------------------- 1 | using ProjectEarthServerAPI.Models.Features; 2 | using ProjectEarthServerAPI.Models.Player; 3 | 4 | namespace ProjectEarthServerAPI.Util 5 | { 6 | public class JournalUtils 7 | { 8 | public static bool UpdateEntry(string playerId, InventoryResponse.BaseItem item) 9 | { 10 | var baseJournal = ReadJournalForPlayer(playerId); 11 | var createEntry = !baseJournal.result.inventoryJournal.ContainsKey(item.id); 12 | 13 | if (createEntry) 14 | { 15 | var entry = new JournalEntry() {firstSeen = item.unlocked.on, lastSeen = item.seen.on}; 16 | 17 | entry.amountCollected = item is InventoryResponse.StackableItem stackableItem 18 | ? (uint)stackableItem.owned 19 | : (uint)((InventoryResponse.NonStackableItem)item).instances.Count; 20 | 21 | baseJournal.result.inventoryJournal.Add(item.id, entry); 22 | } 23 | else 24 | { 25 | var entry = baseJournal.result.inventoryJournal[item.id]; 26 | var itemAmount = item is InventoryResponse.StackableItem stackableItem 27 | ? (uint)stackableItem.owned 28 | : (uint)((InventoryResponse.NonStackableItem)item).instances.Count; 29 | 30 | if (entry.amountCollected > itemAmount) entry.amountCollected = itemAmount; 31 | 32 | entry.lastSeen = item.seen.on; 33 | 34 | baseJournal.result.inventoryJournal[item.id] = entry; 35 | } 36 | 37 | WriteJournalForPlayer(playerId, baseJournal); 38 | 39 | return true; 40 | } 41 | 42 | 43 | public static JournalResponse ReadJournalForPlayer(string playerId) 44 | => GenericUtils.ParseJsonFile(playerId, "journal"); 45 | 46 | public static void WriteJournalForPlayer(string playerId, JournalResponse data) 47 | => GenericUtils.WriteJsonFile(playerId, data, "journal"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/MultiplayerItemConverters.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Linq; 6 | using ProjectEarthServerAPI.Models.Multiplayer; 7 | 8 | namespace ProjectEarthServerAPI.Util 9 | { 10 | public class MultiplayerItemRarityConverter : JsonConverter 11 | { 12 | public override MultiplayerItemRarity ReadJson(JsonReader reader, Type objectType, [AllowNull] MultiplayerItemRarity existingValue, bool hasExistingValue, JsonSerializer serializer) 13 | { 14 | var obj = JObject.ReadFrom(reader); 15 | string rarityString = (string)obj["loc"]; 16 | string rarity = rarityString == "" ? "Invalid" : rarityString.Split(".")[2]; 17 | 18 | return new MultiplayerItemRarity {loc = Enum.Parse(rarity, true), value = (int)Enum.Parse(rarity, true)}; 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, [AllowNull] MultiplayerItemRarity value, JsonSerializer serializer) 22 | { 23 | JObject obj = new JObject {{"loc", "inventory.rarity." + Enum.GetName(value.loc).ToLower()}, {"value", value.value}}; 24 | 25 | obj.WriteTo(writer); 26 | } 27 | } 28 | 29 | public class MultiplayerItemCategoryConverter : JsonConverter 30 | { 31 | public override MultiplayerItemCategory ReadJson(JsonReader reader, Type objectType, [AllowNull] MultiplayerItemCategory existingValue, bool hasExistingValue, JsonSerializer serializer) 32 | { 33 | var obj = JObject.ReadFrom(reader); 34 | string categoryString = (string)obj["loc"]; 35 | string category = categoryString == "" ? "Invalid" : categoryString.Split(".")[2]; 36 | 37 | return new MultiplayerItemCategory {loc = Enum.Parse(category, true), value = (int)Enum.Parse(category, true)}; 38 | } 39 | 40 | public override void WriteJson(JsonWriter writer, [AllowNull] MultiplayerItemCategory value, JsonSerializer serializer) 41 | { 42 | JObject obj = new JObject {{"loc", "inventory.category." + Enum.GetName(value.loc).ToLower()}, {"value", value.value}}; 43 | 44 | obj.WriteTo(writer); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/ProfileUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Dynamic; 2 | using ProjectEarthServerAPI.Models; 3 | 4 | namespace ProjectEarthServerAPI.Util 5 | { 6 | public class ProfileUtils 7 | { 8 | public static ProfileData ReadProfile(string playerId) 9 | { 10 | return GenericUtils.ParseJsonFile(playerId, "profile"); 11 | } 12 | 13 | public static void AddExperienceToPlayer(string playerId, int experiencePoints) 14 | { 15 | var playerProfile = ReadProfile(playerId); 16 | var currentLvl = playerProfile.level; 17 | playerProfile.totalExperience += experiencePoints; 18 | while (currentLvl < 25 && playerProfile.experienceRemaining <= 0) 19 | { 20 | playerProfile.level++; 21 | RewardLevelupRewards(playerId, playerProfile.level); 22 | } 23 | 24 | WriteProfile(playerId, playerProfile); 25 | } 26 | 27 | private static void RewardLevelupRewards(string playerId, int level) 28 | { 29 | RewardUtils.RedeemRewards(playerId, ProfileResponse.levels[level.ToString()].rewards); 30 | } 31 | 32 | private static bool WriteProfile(string playerId, ProfileData playerProfile) 33 | { 34 | return GenericUtils.WriteJsonFile(playerId, playerProfile, "profile"); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/RewardUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using ProjectEarthServerAPI.Models; 3 | 4 | namespace ProjectEarthServerAPI.Util 5 | { 6 | public class RewardUtils 7 | { 8 | public static Updates RedeemRewards(string playerId, Rewards rewards) 9 | { 10 | Updates updates = new Updates(); 11 | uint nextStreamId = GenericUtils.GetNextStreamVersion(); 12 | foreach (var buildplate in rewards.Buildplates) 13 | { 14 | BuildplateUtils.AddToPlayer(playerId, buildplate.Id); 15 | updates.buildplates = nextStreamId; 16 | } 17 | 18 | foreach (var challenge in rewards.Challenges) 19 | { 20 | //ChallengeUtils.AddToPlayer(playerId, challenge.id); 21 | updates.challenges = nextStreamId; 22 | } 23 | 24 | foreach (var item in rewards.Inventory) 25 | { 26 | InventoryUtils.AddItemToInv(playerId, item.Id, item.Amount); 27 | updates.inventory = nextStreamId; 28 | updates.playerJournal = nextStreamId; 29 | } 30 | 31 | foreach (var utilityBlock in rewards.UtilityBlocks) 32 | { 33 | // TODO: This is most likely unused in the actual game, since crafting tables/furnaces dont have ids 34 | } 35 | 36 | foreach (var personaItem in rewards.PersonaItems) 37 | { 38 | // PersonaUtils.AddToPlayer(playerId, personaItem) If we can ever implement CC, this is already in place 39 | } 40 | 41 | if (rewards.ExperiencePoints != null) 42 | { 43 | ProfileUtils.AddExperienceToPlayer(playerId, rewards.ExperiencePoints.Value); 44 | updates.characterProfile = nextStreamId; 45 | } 46 | 47 | return updates; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/RubyUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Newtonsoft.Json; 4 | using ProjectEarthServerAPI.Models; 5 | using ProjectEarthServerAPI.Models.Player; 6 | 7 | namespace ProjectEarthServerAPI.Util 8 | { 9 | public class RubyUtils 10 | { 11 | public static SplitRubyResponse ReadRubies(string playerId) 12 | { 13 | return GenericUtils.ParseJsonFile(playerId, "rubies"); 14 | } 15 | 16 | public static bool WriteRubies(string playerId, SplitRubyResponse ruby) 17 | { 18 | return GenericUtils.WriteJsonFile(playerId, ruby, "rubies"); 19 | } 20 | 21 | public static int SetRubies(string playerId, int count, bool shouldReplaceNotAdd) 22 | { 23 | var origRubies = ReadRubies(playerId); 24 | if (shouldReplaceNotAdd) 25 | { 26 | origRubies.result.earned = count; 27 | } 28 | else 29 | { 30 | origRubies.result.earned += count; 31 | } 32 | 33 | var newRubyNum = origRubies.result.earned; 34 | 35 | WriteRubies(playerId, origRubies); 36 | 37 | return newRubyNum; 38 | } 39 | 40 | public static RubyResponse GetNormalRubyResponse(string playerid) 41 | { 42 | var splitrubies = ReadRubies(playerid); 43 | var response = new RubyResponse() {result = splitrubies.result.earned + splitrubies.result.purchased}; 44 | 45 | return response; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/ServerConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.IO; 6 | using Newtonsoft.Json; 7 | 8 | namespace ProjectEarthServerAPI.Util 9 | { 10 | /// 11 | /// Represents the server configuration as json, while also having a load method 12 | /// 13 | public class ServerConfig 14 | { 15 | //Properties 16 | public string baseServerIP { get; set; } 17 | public string itemsFolderLocation { get; set; } 18 | public string efficiencyCategoriesFolderLocation { get; set; } 19 | public string journalCatalogFileLocation { get; set; } 20 | public string recipesFileLocation { get; set; } 21 | public string settingsFileLocation { get; set; } 22 | public string seasonChallengesFileLocation { get; set; } 23 | public string productCatalogFileLocation { get; set; } 24 | public Dictionary multiplayerAuthKeys { get; set; } 25 | //tappable settings 26 | public int minTappableSpawnAmount { get; set; } 27 | public int maxTappableSpawnAmount { get; set; } 28 | public double tappableSpawnRadius { get; set; } 29 | 30 | //Load method 31 | 32 | /// 33 | /// Get the server config from the configuration file. 34 | /// 35 | /// 36 | public static ServerConfig getFromFile() 37 | { 38 | String file = File.ReadAllText("./data/config/apiconfig.json"); 39 | return JsonConvert.DeserializeObject(file); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/SmeltingUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json.Linq; 5 | using ProjectEarthServerAPI.Models; 6 | using ProjectEarthServerAPI.Models.Features; 7 | using Serilog; 8 | 9 | namespace ProjectEarthServerAPI.Util 10 | { 11 | public class SmeltingUtils 12 | { 13 | private static Recipes recipeList = StateSingleton.Instance.recipes; 14 | private static CatalogResponse catalog = StateSingleton.Instance.catalog; 15 | private static Dictionary> SmeltingJobs = new(); 16 | 17 | public static bool StartSmeltingJob(string playerId, int slot, SmeltingRequest request) // TODO: Check if slot not unlocked (not a big priority) 18 | { 19 | recipeList ??= Recipes.FromFile("./data/recipes"); 20 | var currentDateTime = DateTime.UtcNow; 21 | 22 | var recipe = recipeList.result.smelting.Find(match => match.id == request.RecipeId); 23 | 24 | if (recipe != null) 25 | { 26 | var itemsToReturn = recipe.returnItems.ToList(); 27 | 28 | //InventoryUtils.RemoveItemFromInv(playerId, recipe.inputItemId, null, request.Ingredient.quantity*request.Multiplier); UNCOMMENT/COMMENT THESE LINES TO ENABLE/DISABLE ITEM REMOVALS 29 | 30 | var fuelInfo = new FuelInfo(); 31 | 32 | // InventoryUtils.RemoveItemFromInv(playerId, request.FuelIngredient.itemId, null, request.FuelIngredient.quantity); 33 | var burnInfo = catalog.result.items.Find(match => match.id == request.FuelIngredient.itemId).burnRate; 34 | fuelInfo = new FuelInfo {burnRate = new BurnInfo {burnTime = burnInfo.burnTime * request.FuelIngredient.quantity, heatPerSecond = burnInfo.heatPerSecond}, itemId = request.FuelIngredient.itemId, itemInstanceIds = request.FuelIngredient.itemInstanceIds, quantity = request.FuelIngredient.quantity}; 35 | 36 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 37 | 38 | SmeltingSlotInfo job = new SmeltingSlotInfo 39 | { 40 | available = 0, 41 | boostState = null, 42 | burning = new BurningItems 43 | { 44 | burnStartTime = currentDateTime, 45 | burnsUntil = currentDateTime.AddSeconds(fuelInfo.burnRate.burnTime), 46 | fuel = fuelInfo, 47 | heatDepleted = 0, 48 | remainingBurnTime = new TimeSpan(0, 0, fuelInfo.burnRate.burnTime) 49 | }, 50 | fuel = fuelInfo, 51 | hasSufficientFuel = (recipe.heatRequired <= fuelInfo.burnRate.burnTime * fuelInfo.burnRate.heatPerSecond * fuelInfo.quantity), // Should always be true, requires special handling if false. 52 | heatAppliedToCurrentItem = 0, 53 | completed = 0, 54 | escrow = new InputItem[] {request.Ingredient}, 55 | nextCompletionUtc = null, 56 | output = recipe.output, 57 | recipeId = recipe.id, 58 | sessionId = request.SessionId, 59 | state = "Active", 60 | streamVersion = nextStreamId, 61 | total = request.Multiplier, 62 | totalCompletionUtc = currentDateTime.AddSeconds((double)recipe.heatRequired * request.Multiplier / fuelInfo.burnRate.heatPerSecond), 63 | unlockPrice = null 64 | }; 65 | 66 | job.fuel.quantity = 0; // Mojang pls explain this 67 | 68 | if (request.Multiplier != 1) 69 | { 70 | job.nextCompletionUtc = currentDateTime.AddSeconds((double)recipe.heatRequired / fuelInfo.burnRate.heatPerSecond); 71 | } 72 | 73 | if (!SmeltingJobs.ContainsKey(playerId)) 74 | { 75 | SmeltingJobs.Add(playerId, new Dictionary()); 76 | SmeltingJobs[playerId].Add(1, new SmeltingSlotInfo()); 77 | SmeltingJobs[playerId].Add(2, new SmeltingSlotInfo()); 78 | SmeltingJobs[playerId].Add(3, new SmeltingSlotInfo()); 79 | } 80 | 81 | SmeltingJobs[playerId][slot] = job; 82 | 83 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 84 | 85 | Log.Debug($"[{playerId}]: Initiated smelting job in slot {slot}."); 86 | 87 | return true; 88 | } 89 | 90 | return false; 91 | } 92 | 93 | public static SmeltingSlotResponse GetSmeltingJobInfo(string playerId, int slot) 94 | { 95 | try 96 | { 97 | var currentTime = DateTime.UtcNow; 98 | var job = SmeltingJobs[playerId][slot]; 99 | var recipe = recipeList.result.smelting.Find(match => match.id == job.recipeId & !match.deprecated); 100 | var updates = new Updates(); 101 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 102 | 103 | job.streamVersion = nextStreamId; 104 | 105 | if (job.totalCompletionUtc != null && DateTime.Compare(job.totalCompletionUtc.Value, DateTime.UtcNow) < 0 && job.recipeId != null) 106 | { 107 | job.available = job.total - job.completed; 108 | job.completed += job.available; 109 | job.nextCompletionUtc = null; 110 | job.state = "Completed"; 111 | job.escrow = new InputItem[0]; 112 | } 113 | /*else 114 | { 115 | 116 | job.available++; 117 | //job.completed++; 118 | job.state = "Available"; 119 | job.streamVersion = nextStreamId; 120 | job.nextCompletionUtc = job.nextCompletionUtc.Value.Add(recipe.duration.TimeOfDay); 121 | 122 | for (int i = 0; i < job.escrow.Length - 1; i++) 123 | { 124 | job.escrow[i].quantity -= recipe.ingredients[i].quantity; 125 | } 126 | 127 | }*/ 128 | 129 | if (recipe != null) 130 | { 131 | job.burning.remainingBurnTime = job.burning.burnsUntil.Value.TimeOfDay - currentTime.TimeOfDay; 132 | 133 | job.burning.heatDepleted = (currentTime - job.burning.burnStartTime.Value).TotalSeconds * 134 | job.burning.fuel.burnRate.heatPerSecond; 135 | 136 | job.heatAppliedToCurrentItem = 137 | (float)job.burning.heatDepleted - job.available * recipe.heatRequired; 138 | } 139 | 140 | updates.smelting = nextStreamId; 141 | 142 | 143 | var returnResponse = new SmeltingSlotResponse {result = job, updates = updates}; 144 | 145 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 146 | 147 | Log.Debug($"[{playerId}]: Requested smelting slot {slot} status."); 148 | 149 | return returnResponse; 150 | } 151 | catch (Exception e) 152 | { 153 | Log.Error($"[{playerId}]: Error while getting smelting job info: Smelting Slot: {slot}"); 154 | Log.Debug($"Exception: {e.StackTrace}"); 155 | return null; 156 | } 157 | } 158 | 159 | public static CollectItemsResponse FinishSmeltingJob(string playerId, int slot) 160 | { 161 | var job = SmeltingJobs[playerId][slot]; 162 | var recipe = recipeList.result.smelting.Find(match => match.id == job.recipeId & !match.deprecated); 163 | var currentTime = DateTime.UtcNow; 164 | int craftedAmount = 0; 165 | 166 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 167 | 168 | var returnResponse = new CollectItemsResponse {result = new CollectItemsInfo {rewards = new Rewards(),}, updates = new Updates()}; 169 | 170 | if (job.completed != job.total && job.nextCompletionUtc != null) 171 | { 172 | if (DateTime.UtcNow >= job.nextCompletionUtc) 173 | { 174 | craftedAmount++; 175 | while (DateTime.UtcNow >= job.nextCompletionUtc && job.nextCompletionUtc.Value.AddSeconds((double)recipe.heatRequired / job.burning.fuel.burnRate.heatPerSecond) < job.totalCompletionUtc && craftedAmount < job.total - job.completed) 176 | { 177 | job.nextCompletionUtc = job.nextCompletionUtc.Value.AddSeconds((double)recipe.heatRequired / job.burning.fuel.burnRate.heatPerSecond); 178 | craftedAmount++; 179 | } 180 | 181 | job.nextCompletionUtc = job.nextCompletionUtc.Value.AddSeconds((double)recipe.heatRequired / job.burning.fuel.burnRate.heatPerSecond); 182 | job.completed += craftedAmount; 183 | //job.available -= craftedAmount; 184 | foreach (var inputItem in job.escrow) 185 | { 186 | inputItem.quantity -= 1; 187 | } 188 | 189 | job.streamVersion = nextStreamId; 190 | 191 | InventoryUtils.AddItemToInv(playerId, job.output.itemId, job.output.quantity * craftedAmount); 192 | } 193 | } 194 | else 195 | { 196 | craftedAmount = job.available; 197 | InventoryUtils.AddItemToInv(playerId, job.output.itemId, job.output.quantity * craftedAmount); 198 | // TODO: Add to challenges, tokens, journal (when implemented) 199 | } 200 | 201 | if (!TokenUtils.GetTokenResponseForUserId(playerId).Result.tokens.Any(match => match.Value.clientProperties.ContainsKey("itemid") && match.Value.clientProperties["itemid"] == job.output.itemId.ToString())) 202 | { 203 | //TokenUtils.AddItemToken(playerId, job.output.itemId); -> List of item tokens not known. Could cause issues later, for now we just disable it. 204 | returnResponse.updates.tokens = nextStreamId; 205 | } 206 | 207 | returnResponse.result.rewards.Inventory = returnResponse.result.rewards.Inventory.Append(new RewardComponent {Amount = job.output.quantity * craftedAmount, Id = job.output.itemId}).ToArray(); 208 | 209 | returnResponse.updates.smelting = nextStreamId; 210 | returnResponse.updates.inventory = nextStreamId; 211 | returnResponse.updates.playerJournal = nextStreamId; 212 | 213 | 214 | if (job.completed == job.total || job.nextCompletionUtc == null) 215 | { 216 | job.burning.remainingBurnTime = new TimeSpan((job.burning.burnsUntil - currentTime.TimeOfDay).Value.Ticks); 217 | job.burning.heatDepleted = (currentTime - job.burning.burnStartTime.Value).TotalSeconds * 218 | job.burning.fuel.burnRate.heatPerSecond; 219 | 220 | job.fuel = null; 221 | job.heatAppliedToCurrentItem = null; 222 | job.hasSufficientFuel = null; 223 | job.nextCompletionUtc = null; 224 | job.available = 0; 225 | job.completed = 0; 226 | job.recipeId = null; 227 | job.sessionId = null; 228 | job.state = "Empty"; 229 | job.total = 0; 230 | job.boostState = null; 231 | job.totalCompletionUtc = null; 232 | job.unlockPrice = null; 233 | job.output = null; 234 | job.streamVersion = nextStreamId; 235 | } 236 | 237 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 238 | 239 | Log.Debug($"[{playerId}]: Collected results of smelting slot {slot}."); 240 | 241 | return returnResponse; 242 | } 243 | 244 | public static bool CancelSmeltingJob(string playerId, int slot) 245 | { 246 | var job = SmeltingJobs[playerId][slot]; 247 | var currentTime = DateTime.UtcNow; 248 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 249 | 250 | foreach (InputItem item in job.escrow) 251 | { 252 | InventoryUtils.AddItemToInv(playerId, item.itemId, item.quantity); 253 | } 254 | 255 | job.burning.remainingBurnTime = new TimeSpan((job.burning.burnsUntil - currentTime.TimeOfDay).Value.Ticks); 256 | job.burning.heatDepleted = (currentTime - job.burning.burnStartTime.Value).TotalSeconds * 257 | job.burning.fuel.burnRate.heatPerSecond; 258 | 259 | job.burning.burnStartTime = null; 260 | job.burning.burnsUntil = null; 261 | 262 | job.fuel = null; 263 | job.heatAppliedToCurrentItem = null; 264 | job.hasSufficientFuel = null; 265 | job.nextCompletionUtc = null; 266 | job.available = 0; 267 | job.completed = 0; 268 | job.recipeId = null; 269 | job.sessionId = null; 270 | job.state = "Empty"; 271 | job.total = 0; 272 | job.boostState = null; 273 | job.totalCompletionUtc = null; 274 | job.unlockPrice = null; 275 | job.output = null; 276 | job.streamVersion = nextStreamId; 277 | 278 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 279 | 280 | Log.Debug($"[{playerId}]: Cancelled smelting job in slot {slot}."); 281 | 282 | return true; 283 | } 284 | 285 | public static CraftingUpdates UnlockSmeltingSlot(string playerId, int slot) 286 | { 287 | var job = SmeltingJobs[playerId][slot]; 288 | 289 | RubyUtils.SetRubies(playerId, job.unlockPrice.cost - job.unlockPrice.discount, false); 290 | job.state = "Empty"; 291 | job.unlockPrice = null; 292 | 293 | var nextStreamId = GenericUtils.GetNextStreamVersion(); 294 | var returnUpdates = new CraftingUpdates {updates = new Updates()}; 295 | 296 | UtilityBlockUtils.UpdateUtilityBlocks(playerId, slot, job); 297 | 298 | Log.Debug($"[{playerId}]: Unlocked smelting slot {slot}."); 299 | 300 | returnUpdates.updates.smelting = nextStreamId; 301 | 302 | return returnUpdates; 303 | } 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/StateSingleton.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using ProjectEarthServerAPI.Models; 6 | using ProjectEarthServerAPI.Util; 7 | using ProjectEarthServerAPI.Models.Features; 8 | using ProjectEarthServerAPI.Models.Player; 9 | 10 | namespace ProjectEarthServerAPI.Util 11 | { 12 | /// 13 | /// Global state information 14 | /// 15 | public sealed class StateSingleton 16 | { 17 | private StateSingleton() { } 18 | 19 | private static StateSingleton instance = null; 20 | 21 | public static StateSingleton Instance 22 | { 23 | get 24 | { 25 | if (instance == null) 26 | { 27 | instance = new StateSingleton(); 28 | } 29 | 30 | return instance; 31 | } 32 | } 33 | 34 | public CatalogResponse catalog { get; set; } 35 | public ServerConfig config { get; set; } 36 | public Recipes recipes { get; set; } 37 | public SettingsResponse settings { get; set; } 38 | public ChallengesResponse seasonChallenges { get; set; } 39 | public ProductCatalogResponse productCatalog { get; set; } 40 | 41 | public Dictionary>> tappableData { get; set; } 42 | 43 | /// 44 | /// A reference of guid <-> id, so that we can keep track of a tappable from spawn to redeem 45 | /// 46 | public Dictionary activeTappableTypes { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/StringToUuidConv.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using Uma.Uuid; 5 | 6 | namespace ProjectEarthServerAPI.Util 7 | { 8 | // TypeConverter from string to Uuid, useful for directly storing uuids instead of strings 9 | public class StringToUuidConv : TypeConverter 10 | { 11 | public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) 12 | { 13 | return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); 14 | } 15 | 16 | public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 17 | { 18 | return destinationType == typeof(string) || base.CanConvertTo(context, destinationType); 19 | } 20 | 21 | public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value) 22 | { 23 | if (value is string str) 24 | { 25 | return new Uuid(str); 26 | } 27 | 28 | return base.ConvertFrom(context, culture, value); 29 | } 30 | 31 | public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) 32 | { 33 | if (destinationType == typeof(string) && value is Uuid uuid) 34 | { 35 | return uuid.ToString(); 36 | } 37 | 38 | return base.ConvertTo(context, culture, value, destinationType); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/TappableUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Newtonsoft.Json; 5 | using ProjectEarthServerAPI.Models; 6 | using ProjectEarthServerAPI.Models.Features; 7 | using Serilog; 8 | using Uma.Uuid; 9 | 10 | namespace ProjectEarthServerAPI.Util 11 | { 12 | /// 13 | /// Some simple utilities to interface with generated files from Tappy 14 | /// 15 | public class TappableUtils 16 | { 17 | private static Version4Generator version4Generator = new Version4Generator(); 18 | 19 | // TODO: Consider turning this into a dictionary (or pull it out to a separate file) and building out a spawn-weight system? 20 | public static string[] TappableTypes = new[] 21 | { 22 | "genoa:stone_mound_a_tappable_map", "genoa:stone_mound_b_tappable_map", 23 | "genoa:stone_mound_c_tappable_map", "genoa:grass_mound_a_tappable_map", 24 | "genoa:grass_mound_b_tappable_map", "genoa:grass_mound_c_tappable_map", "genoa:tree_oak_a_tappable_map", 25 | "genoa:tree_oak_b_tappable_map", "genoa:tree_oak_c_tappable_map", "genoa:tree_birch_a_tappable_map", 26 | "genoa:tree_spruce_a_tappable_map", "genoa:chest_tappable_map", "genoa:sheep_tappable_map", 27 | "genoa:cow_tappable_map", "genoa:pig_tappable_map", "genoa:chicken_tappable_map" 28 | }; 29 | 30 | private static Random random = new Random(); 31 | 32 | // For json deserialization 33 | public class TappableLootTable 34 | { 35 | public string tappableID { get; set; } 36 | public List> possibleDropSets { get; set; } 37 | } 38 | 39 | public static Dictionary>> loadAllTappableSets() 40 | { 41 | Log.Information("[Tappables] Loading tappable data."); 42 | Dictionary>> tappableData = new Dictionary>>(); 43 | string[] files = Directory.GetFiles("./data/tappable", "*.json"); 44 | foreach (var file in files) 45 | { 46 | TappableLootTable table = JsonConvert.DeserializeObject(File.ReadAllText(file)); 47 | tappableData.Add(table.tappableID, table.possibleDropSets); 48 | Log.Information($"Loaded {table.possibleDropSets.Count} drop sets for tappable ID {table.tappableID} | Path: {file}"); 49 | } 50 | 51 | return tappableData; 52 | } 53 | 54 | /// 55 | /// Generate a new tappable in a given radius of a given cord set 56 | /// 57 | /// 58 | /// 59 | /// Optional. Spawn Radius if not provided, will default to value specified in config 60 | /// Optional. If not provided, a random type will be picked from TappableUtils.TappableTypes 61 | /// 62 | //double is default set to negative because its *extremely unlikely* someone will set a negative value intentionally, and I can't set it to null. 63 | public static LocationResponse.ActiveLocation createTappableInRadiusOfCoordinates(double longitude, double latitude, double radius = -1.0, string type = null) 64 | { 65 | //if null we do random 66 | type ??= TappableUtils.TappableTypes[random.Next(0, TappableUtils.TappableTypes.Length)]; 67 | if (radius == -1.0) 68 | { 69 | radius = StateSingleton.Instance.config.tappableSpawnRadius; 70 | } 71 | 72 | var currentTime = DateTime.UtcNow; 73 | 74 | //Nab tile loc 75 | int[] cords = Tile.getTileForCords(latitude, longitude); 76 | LocationResponse.ActiveLocation tappable = new LocationResponse.ActiveLocation 77 | { 78 | id = Guid.NewGuid().ToString(), // Generate a random GUID for the tappable 79 | tileId = cords[0] + "_" + cords[1], 80 | coordinate = new Coordinate 81 | { 82 | latitude = Math.Round(latitude + random.NextDouble() * radius, 6), // Round off for the client to be happy 83 | longitude = Math.Round(longitude + random.NextDouble() * radius, 6) 84 | }, 85 | spawnTime = currentTime, 86 | expirationTime = currentTime.AddMinutes(10), //Packet captures show that typically earth keeps Tappables around for 10 minutes 87 | type = "Tappable", // who wouldve guessed? 88 | icon = type, 89 | metadata = new LocationResponse.Metadata 90 | { 91 | rarity = Item.Rarity.Common, 92 | rewardId = version4Generator.NewUuid().ToString() // Seems to always be uuidv4 from official responses so generate one 93 | }, 94 | encounterMetadata = null, //working captured responses have this, its fine 95 | tappableMetadata = new LocationResponse.TappableMetadata 96 | { 97 | rarity = Item.Rarity.Common //assuming this and the above need to allign. Why have 2 occurances? who knows. 98 | } 99 | }; 100 | 101 | return tappable; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/Tile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Threading.Tasks; 7 | 8 | namespace ProjectEarthServerAPI.Util 9 | { 10 | /// 11 | /// Contains Functions for converting long/lat -> tile pos, downloading tilees, and anything else that might come up 12 | /// 13 | public class Tile 14 | { 15 | public static bool DownloadTile(int pos1, int pos2, string basePath) 16 | { 17 | WebClient webClient = new WebClient(); 18 | 19 | try 20 | { 21 | Directory.CreateDirectory(Path.Combine(basePath, pos1.ToString())); 22 | string downloadUrl = "https://cdn.mceserv.net/tile/16/" + pos1 + "/" + pos1 + "_" + pos2 + "_16.png"; 23 | //string downloadUrl = "https://tiles.projectearth.dev/styles/mc-earth/16/" + pos1 + "/" + pos2 + ".png"; // Disabled until aliasing issues are fixed 24 | webClient.DownloadFile(downloadUrl, Path.Combine(basePath, pos1.ToString(), $"{pos1}_{pos2}_16.png")); 25 | webClient.Dispose(); 26 | return true; 27 | } 28 | catch (WebException wex) 29 | { 30 | //TODO: error 502 check. 31 | webClient.Dispose(); 32 | return false; 33 | } 34 | } 35 | 36 | //From https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames with slight changes 37 | 38 | public static int[] getTileForCords(double lat, double lon) 39 | { 40 | //Adapted from java example. Zoom is replaced by the constant 16 because all MCE tiles are at zoom 16 41 | 42 | int xtile = (int)Math.Floor((lon + 180) / 360 * (1 << 16)); 43 | int ytile = (int)Math.Floor((1 - Math.Log(Math.Tan(toRadians(lat)) + 1 / Math.Cos(toRadians(lat))) / Math.PI) / 2 * (1 << 16)); 44 | 45 | if (xtile < 0) 46 | xtile = 0; 47 | if (xtile >= (1 << 16)) 48 | xtile = ((1 << 16) - 1); 49 | if (ytile < 0) 50 | ytile = 0; 51 | if (ytile >= (1 << 16)) 52 | ytile = ((1 << 16) - 1); 53 | 54 | return new int[] {xtile, ytile}; 55 | } 56 | 57 | //Helper 58 | static double toRadians(double angle) 59 | { 60 | return (Math.PI / 180) * angle; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/TokenUtils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Newtonsoft.Json; 6 | using ProjectEarthServerAPI.Models; 7 | using Serilog; 8 | using Uma.Uuid; 9 | 10 | namespace ProjectEarthServerAPI.Util 11 | { 12 | public class TokenUtils 13 | { 14 | private static readonly Version4Generator Version4Generator = new(); 15 | 16 | public static Dictionary GetSigninTokens(string playerId) 17 | { 18 | var origTokens = GetTokensForUserId(playerId); 19 | Dictionary returnTokens = new Dictionary(); 20 | foreach (KeyValuePair tok in origTokens) 21 | { 22 | if (tok.Value.clientProperties.Count == 0) 23 | { 24 | returnTokens.Add(tok.Key, tok.Value); 25 | } 26 | } 27 | 28 | return returnTokens; 29 | } 30 | 31 | public static Dictionary GetTokensForUserId(string playerId) 32 | { 33 | var serializedTokens = GetTokenResponseForUserId(playerId); 34 | return serializedTokens.Result.tokens; 35 | } 36 | 37 | public static TokenResponse GetTokenResponseForUserId(string playerId) 38 | { 39 | var returntokens = GetSerializedTokenResponse(playerId); 40 | 41 | var serializedTokens = JsonConvert.DeserializeObject(returntokens, new JsonSerializerSettings {NullValueHandling = NullValueHandling.Ignore}); 42 | return serializedTokens; 43 | } 44 | 45 | public static string GetSerializedTokenResponse(string playerId) 46 | { 47 | return JsonConvert.SerializeObject( 48 | GenericUtils.ParseJsonFile(playerId, "tokens")); 49 | } 50 | 51 | public static void AddItemToken(string playerId, Guid itemId) 52 | { 53 | var tokenlist = GetTokensForUserId(playerId); 54 | 55 | if (tokenlist.All(pred => 56 | pred.Value.clientProperties.Count == 0 57 | || !pred.Value.clientProperties.ContainsKey("itemid") 58 | || pred.Value.clientProperties["itemid"] != itemId.ToString())) 59 | { 60 | var itemtoken = new Token {clientProperties = new Dictionary(), clientType = "item.unlocked", lifetime = "Persistent", rewards = new Rewards()}; 61 | 62 | itemtoken.clientProperties.Add("itemid", itemId.ToString()); 63 | 64 | tokenlist.Add(Version4Generator.NewUuid(), itemtoken); 65 | 66 | Log.Information($"[{playerId}]: Added item token {itemId}!"); 67 | WriteTokensForPlayer(playerId, tokenlist); 68 | } 69 | } 70 | 71 | public static bool AddToken(string playerId, Token tokenToAdd) 72 | { 73 | var tokenlist = GetTokensForUserId(playerId); 74 | if (!tokenlist.ContainsValue(tokenToAdd)) 75 | { 76 | tokenlist.Add(Version4Generator.NewUuid(), tokenToAdd); 77 | return true; 78 | } 79 | else return false; 80 | } 81 | 82 | public static Token RedeemToken(string playerId, Uuid tokenId) 83 | { 84 | var parsedTokens = GetTokenResponseForUserId(playerId); 85 | if (parsedTokens.Result.tokens.ContainsKey(tokenId)) 86 | { 87 | var tokenToRedeem = parsedTokens.Result.tokens[tokenId]; 88 | RewardUtils.RedeemRewards(playerId, tokenToRedeem.rewards); 89 | 90 | parsedTokens.Result.tokens.Remove(tokenId); 91 | 92 | Log.Information($"[{playerId}]: Redeemed token {tokenId}."); 93 | 94 | return tokenToRedeem; 95 | } 96 | 97 | return null; 98 | } 99 | 100 | private static void WriteTokensForPlayer(string playerId, Dictionary tokenlist) 101 | { 102 | var tokenResp = new TokenResponse {Result = new TokenResult {tokens = tokenlist}, updates = new Updates()}; 103 | GenericUtils.WriteJsonFile(playerId, tokenResp, "tokens"); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/Util/UtilityBlockUtils.cs: -------------------------------------------------------------------------------- 1 | using ProjectEarthServerAPI.Models.Features; 2 | 3 | namespace ProjectEarthServerAPI.Util 4 | { 5 | public class UtilityBlockUtils 6 | { 7 | public static UtilityBlocksResponse ReadUtilityBlocks(string playerId) 8 | { 9 | return GenericUtils.ParseJsonFile(playerId, "utilityBlocks"); 10 | } 11 | 12 | public static bool WriteUtilityBlocks(string playerId, UtilityBlocksResponse obj) 13 | { 14 | return GenericUtils.WriteJsonFile(playerId, obj, "utilityBlocks"); 15 | } 16 | 17 | public static bool UpdateUtilityBlocks(string playerId, int slot, CraftingSlotInfo job) 18 | { 19 | var currentUtilBlocks = ReadUtilityBlocks(playerId); 20 | currentUtilBlocks.result.crafting[slot.ToString()] = job; 21 | currentUtilBlocks.result.crafting["2"].streamVersion = job.streamVersion; 22 | currentUtilBlocks.result.crafting["3"].streamVersion = job.streamVersion; 23 | 24 | WriteUtilityBlocks(playerId, currentUtilBlocks); 25 | 26 | return true; 27 | } 28 | 29 | public static bool UpdateUtilityBlocks(string playerId, int slot, SmeltingSlotInfo job) 30 | { 31 | var currentUtilBlocks = ReadUtilityBlocks(playerId); 32 | currentUtilBlocks.result.smelting[slot.ToString()] = job; 33 | currentUtilBlocks.result.smelting["2"].streamVersion = job.streamVersion; 34 | currentUtilBlocks.result.smelting["3"].streamVersion = job.streamVersion; 35 | 36 | WriteUtilityBlocks(playerId, currentUtilBlocks); 37 | 38 | return true; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ProjectEarthServerAPI/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Kestrel": { 3 | "EndPoints": { 4 | "Http": { 5 | "Url": "http://*:80" 6 | } 7 | } 8 | }, 9 | "Logging": { 10 | "LogLevel": { 11 | "Default": "Information", 12 | "Microsoft": "Warning", 13 | "Microsoft.Hosting.Lifetime": "Information" 14 | } 15 | }, 16 | "AllowedHosts": "*" 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Project Earth Api 2 | *The core API for Project Earth* 3 | 4 | ## What does this component do? 5 | The core API handles the bulk of game functionality - pretty much everything that isn't direct AR gameplay is done here. 6 | 7 | ## Building 8 | - Place the config in the same folder as whereever your built executable is going to end up 9 | - Open the sln in your IDE of choice 10 | - Build & run 11 | 12 | ## Setting up the Project Earth server infrastructure. 13 | 14 | ### Getting all the parts 15 | 16 | to start, ensure that you have built copies of all the required components downloaded: 17 | - A built copy of the Api (you are in this repo), which you can fetch from [our jenkins](https://ci.rtm516.co.uk/job/ProjectEarth/job/Api/job/master/) 18 | - Our [ApiData](https://github.com/Project-Earth-Team/ApiData) repo, or your own data. In addition, you'll need the Minecraft Earth resource pack file, renamed to `vanilla.zip` and placed in the `resourcepacks` subfolder of the ApiData repo. You can procure the resourcepack from [here](https://cdn.mceserv.net/availableresourcepack/resourcepacks/dba38e59-091a-4826-b76a-a08d7de5a9e2-1301b0c257a311678123b9e7325d0d6c61db3c35), provided you're setting up before June 30th, 2021. Rename your clone to `data`, and place it next to your Api executable. 19 | - Our fork of [Cloudburst](https://github.com/Project-Earth-Team/Server). Builds of this can be found [here](https://ci.rtm516.co.uk/job/ProjectEarth/job/Server/job/earth-inventory/). This jar can be located elsewhere from the Api things. 20 | - Run Cloudburst once to generate the file structure. 21 | - In the plugins folder, you'll need [GenoaPlugin](https://github.com/Project-Earth-Team/GenoaPlugin), and [GenoaAllocatorPlugin](https://github.com/Project-Earth-Team/GenoaAllocatorPlugin). The CI for this can be found [here](https://ci.rtm516.co.uk/job/ProjectEarth/job/GenoaPlugin/job/master/) and [here](https://ci.rtm516.co.uk/job/ProjectEarth/job/GenoaAllocatorPlugin/job/main/). **Note: make sure to rename your GenoaAllocatorPlugin.jar to ZGenoaAllocatorPlugin.jar, or you will run into issues with class loading** 22 | 23 | ### Setting up 24 | 25 | On the cloudburst side: 26 | - within the `plugins` folder, create a `GenoaAllocatorPlugin` folder, and in there, make a `key.txt` file containing a base64 encryption key. An example key is 27 | ``` 28 | /g1xCS33QYGC+F2s016WXaQWT8ICnzJvdqcVltNtWljrkCyjd5Ut4tvy2d/IgNga0uniZxv/t0hELdZmvx+cdA== 29 | ``` 30 | - edit the cloudburst.yml file, and chan ge the core api url to the url your Api will be accessible from 31 | - on the Api side, go to `data/config/apiconfig.json`, and add the following: 32 | ```json 33 | "multiplayerAuthKeys": { 34 | "Your cloudburst server IP here": "the same key you put in key.txt earlier" 35 | } 36 | ``` 37 | - Start up the Api 38 | - Start up cloudburst. After a short while the Api should mention a server being connected. 39 | - If you run into issues, retrace your steps, or [contact us on discord](https://discord.gg/Zf9aYZACU4) 40 | - If everything works, your next challenge is to get Minecraft Earth to talk to your Api. If you're on Android, you can utilize [our patcher](https://github.com/Project-Earth-Team/PatcherApp). If you're on IOS, the only way to accomplish this without jailbreak is to utilize a DNS, such as bind9. Setup for that goes beyond the scope of this guide. 41 | 42 | 43 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | dotnet publish -r linux-x64 -p:PublishSingleFile=true --self-contained true 2 | --------------------------------------------------------------------------------