├── GTGrimServer ├── libman.json ├── Resources │ ├── news │ │ ├── dev │ │ │ └── gb │ │ │ │ ├── 600.xml │ │ │ │ ├── 601.xml │ │ │ │ ├── l1_1.xml │ │ │ │ ├── l1_2.xml │ │ │ │ ├── root.xml │ │ │ │ ├── l1_0.xml │ │ │ │ ├── l0_0.xml │ │ │ │ └── 500.xml │ │ └── common │ │ │ └── 500 │ │ │ ├── 0.jpg │ │ │ ├── 1.jpg │ │ │ └── 2.jpg │ ├── event │ │ └── dev │ │ │ ├── rick.png │ │ │ ├── setting.xml │ │ │ └── event_list_gt5.xml │ ├── course │ │ └── data │ │ │ ├── 1001000.dat │ │ │ └── 1001001.dat │ ├── greeting │ │ └── dev │ │ │ └── gb │ │ │ ├── 1.img │ │ │ └── greeting_list.xml │ ├── data │ │ └── tv │ │ │ └── common │ │ │ └── tv2_l_m_4.img │ ├── regionlist.xml │ ├── data2 │ │ ├── museum │ │ │ └── dev │ │ │ │ └── gb │ │ │ │ ├── museum_gb_l_1.xml │ │ │ │ └── museum_gb_1.xml │ │ └── tv │ │ │ └── dev │ │ │ └── gb │ │ │ ├── tv2_gb_root.xml │ │ │ ├── tv2_gb_s_1.xml │ │ │ ├── tv2_gb_l_2.xml │ │ │ └── tv2_gb_1.xml │ ├── patch │ │ └── patchinfo.xml │ ├── used_car │ │ └── dev │ │ │ └── used_car_list.xml │ ├── serverlist.xml │ ├── dev │ │ └── serverlist.xml │ └── serverlist_gt5.xml ├── appsettings.json ├── Models │ ├── Xml │ │ ├── RegionList.cs │ │ ├── ServerList.cs │ │ ├── SpecialList.cs │ │ ├── ItemBoxList.cs │ │ ├── PresenceList.cs │ │ ├── BSpecCarList.cs │ │ ├── SimpleFriendList.cs │ │ ├── RankingList.cs │ │ ├── TicketResult.cs │ │ ├── MailList.cs │ │ ├── PhotoList.cs │ │ ├── BbsCommentList.cs │ │ ├── GrimResult.cs │ │ ├── GrimRequest.cs │ │ ├── ActionLogList.cs │ │ ├── CourseList.cs │ │ └── UserProfile.cs │ ├── SessionToken.cs │ └── Player.cs ├── appsettings.Development.json ├── Config │ └── GameServerOptions.cs ├── StatusCodes │ ├── BanResult.cs │ ├── SuspendedResult.cs │ ├── ConsoleBanResult.cs │ ├── ConsoleBanObjectResult.cs │ ├── BanObjectResult.cs │ └── SuspendedObjectResult.cs ├── Database │ ├── Tables │ │ ├── FriendDTO.cs │ │ ├── BbsDTO.cs │ │ ├── PhotoDTO.cs │ │ ├── UserSpecialDTO.cs │ │ ├── ActionLogDTO.cs │ │ ├── CourseDTO.cs │ │ └── UserDTO.cs │ └── Controllers │ │ ├── IDBAccesser.cs │ │ ├── BbsBoardDBManager.cs │ │ ├── ActionLogDBManager.cs │ │ ├── UserSpecialDBManager.cs │ │ ├── CourseDBManager.cs │ │ ├── PhotoDBManager.cs │ │ ├── FriendDBManager.cs │ │ └── UserDBManager.cs ├── Utils │ ├── StaticFileUtil.cs │ ├── DateTimeExtensions.cs │ └── XmlSerializerOutputFormatterNamespace.cs ├── Properties │ └── launchSettings.json ├── Program.cs ├── Controllers │ ├── RegionListController.cs │ ├── PatchController.cs │ ├── ServerListController.cs │ ├── Profiles │ │ ├── ExtendSecondController.cs │ │ ├── ItemBoxController.cs │ │ ├── LogController.cs │ │ ├── LocaleController.cs │ │ ├── BSpecController.cs │ │ ├── RankingController.cs │ │ ├── GTMailController.cs │ │ ├── CourseController.cs │ │ └── BbsController.cs │ ├── UsedCarDealershipController.cs │ ├── GrimControllerBase.cs │ ├── GreetingController.cs │ ├── Data │ │ ├── MuseumController.cs │ │ └── TVController.cs │ ├── EventController.cs │ ├── NewsController.cs │ └── UserController.cs ├── Filters │ └── PDIClientAttribute.cs ├── GTGrimServer.csproj ├── Services │ └── PlayerManager.cs ├── Sony │ └── NPTicket.cs ├── GTConstants.cs └── Startup.cs ├── README.md ├── LICENSE ├── GTGrimServer.sln ├── .gitattributes ├── REGARDING_OTHER_ONLINE_SERVERS.md └── .gitignore /GTGrimServer/libman.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0", 3 | "defaultProvider": "cdnjs", 4 | "libraries": [] 5 | } -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/600.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/601.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/l1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/l1_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/event/dev/rick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/event/dev/rick.png -------------------------------------------------------------------------------- /GTGrimServer/Resources/course/data/1001000.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/course/data/1001000.dat -------------------------------------------------------------------------------- /GTGrimServer/Resources/course/data/1001001.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/course/data/1001001.dat -------------------------------------------------------------------------------- /GTGrimServer/Resources/greeting/dev/gb/1.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/greeting/dev/gb/1.img -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/common/500/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/news/common/500/0.jpg -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/common/500/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/news/common/500/1.jpg -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/common/500/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/news/common/500/2.jpg -------------------------------------------------------------------------------- /GTGrimServer/Resources/data/tv/common/tv2_l_m_4.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Nenkai/Grimoire/HEAD/GTGrimServer/Resources/data/tv/common/tv2_l_m_4.img -------------------------------------------------------------------------------- /GTGrimServer/Resources/regionlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/museum/dev/gb/museum_gb_l_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/greeting/dev/gb/greeting_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/root.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GTGrimServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/tv/dev/gb/tv2_gb_root.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/event/dev/setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 200 5 | 200 6 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/museum/dev/gb/museum_gb_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 123 5 | bmw 6 | 2021 7 | Just a caption test 8 | 2021-04-02T20:20:27+00:00 9 | 12345 10 | 1 11 | 12 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/RegionList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Serialization; 3 | 4 | namespace GTGrimServer.Models.Xml 5 | { 6 | [XmlRoot("region")] 7 | public class RegionList 8 | { 9 | [XmlAttribute("key")] 10 | public string RegionCode { get; set; } 11 | 12 | [XmlAttribute("value")] 13 | public string RegionName { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /GTGrimServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | 10 | "Urls": "http://*:80", 11 | "GameServer": { 12 | "GameType": "GT5", 13 | "XmlResourcePath": "Resources", 14 | "EnforceGameVersion": "False" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTGrimServer/Config/GameServerOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Config 7 | { 8 | public class GameServerOptions 9 | { 10 | public const string GameServer = "GameServer"; 11 | 12 | public GameType GameType { get; set; } 13 | public string XmlResourcePath { get; set; } 14 | public bool EnforceGameVersion { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/BanResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class BanResult : StatusCodeResult 12 | { 13 | public const int DefaultStatusCode = 499; 14 | public BanResult() : base(DefaultStatusCode) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/l1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/SuspendedResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class SuspendedResult : StatusCodeResult 12 | { 13 | public const int DefaultStatusCode = 499; 14 | public SuspendedResult() : base(DefaultStatusCode) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/ConsoleBanResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class ConsoleBanResult : StatusCodeResult 12 | { 13 | public const int DefaultStatusCode = 497; 14 | public ConsoleBanResult() : base(DefaultStatusCode) { } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/patch/patchinfo.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | --> 8 | 9 | 10 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/ConsoleBanObjectResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class BanObjectResult : ObjectResult 12 | { 13 | public const int DefaultStatusCode = 499; 14 | public BanObjectResult(object value) 15 | : base(value) 16 | { 17 | StatusCode = DefaultStatusCode; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/BanObjectResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class ConsoleBanObjectResult : ObjectResult 12 | { 13 | public const int DefaultStatusCode = 497; 14 | public ConsoleBanObjectResult(object value) 15 | : base(value) 16 | { 17 | StatusCode = DefaultStatusCode; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GTGrimServer/StatusCodes/SuspendedObjectResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.Infrastructure; 7 | 8 | namespace GTGrimServer.Results 9 | { 10 | [DefaultStatusCode(DefaultStatusCode)] 11 | public class SuspendedObjectResult : ObjectResult 12 | { 13 | public const int DefaultStatusCode = 499; 14 | public SuspendedObjectResult(object value) 15 | : base(value) 16 | { 17 | StatusCode = DefaultStatusCode; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/l0_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/FriendDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class FriendDTO 9 | { 10 | /// 11 | /// Row Id 12 | /// 13 | public long Id { get; set; } 14 | 15 | /// 16 | /// Database Id of the main user. 17 | /// 18 | public long UserId { get; set; } 19 | 20 | /// 21 | /// Database Id of the friend. 22 | /// 23 | public long FriendId { get; set; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /GTGrimServer/Models/SessionToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Models 7 | { 8 | /// 9 | /// A session token to grant authorization for a player. 10 | /// 11 | public class SessionToken 12 | { 13 | /// 14 | /// Actual token string. 15 | /// 16 | public string Token { get; set; } 17 | 18 | /// 19 | /// Expiration date of the token. 20 | /// 21 | public DateTime ExpiryDate { get; set; } 22 | 23 | public SessionToken(string token, DateTime expiry) 24 | { 25 | Token = token; 26 | ExpiryDate = expiry; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grimoire 2 | An Open-source, work in progress Gran Turismo 5/6 Custom Server. 3 | 4 | **Most of the actual work is being done in private until completed. This is just a proof of concept.** 5 | 6 | ### ⚠️ [Any project using this source code is not endorsed, unwanted, and strongly advised against. It is not remotely ready for production, has no safety precautions, and in the cases of using it on real hardware, can and WILL cause PSN bans by simply reporting names. These includes the GT6 "revivals" that can be found around using this code.](https://github.com/Nenkai/Grimoire/blob/master/REGARDING_OTHER_ONLINE_SERVERS.md) 7 | 8 | ## State of the project 9 | **Heavily unfinished, can break easily.** 10 | So far the server allows GT5 and GT6 players to login, certain community features are supported but not all. Everything is still very stub and very likely subject to change 11 | -------------------------------------------------------------------------------- /GTGrimServer/Utils/StaticFileUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Http; 6 | using Microsoft.AspNetCore; 7 | using Microsoft.AspNetCore.Mvc; 8 | 9 | namespace GTGrimServer.Utils 10 | { 11 | public static class StaticFileUtil 12 | { 13 | public static async Task SendFile(this ControllerBase cBase, string mainDir, string fileName) 14 | { 15 | string file = System.IO.Path.Combine(mainDir, fileName); 16 | if (!System.IO.File.Exists(file)) 17 | { 18 | cBase.Response.StatusCode = StatusCodes.Status404NotFound; 19 | return; 20 | } 21 | 22 | using var fs = System.IO.File.OpenRead(file); 23 | await fs.CopyToAsync(cBase.Response.Body); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/ServerList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.ComponentModel; 6 | using System.Xml.Serialization; 7 | 8 | namespace GTGrimServer.Models.Xml 9 | { 10 | [XmlRoot("servers")] 11 | public class ServerList 12 | { 13 | [XmlElement("server")] 14 | public Server[] Servers { get; set; } 15 | } 16 | 17 | [XmlRoot("server")] 18 | public class Server 19 | { 20 | [XmlElement("name")] 21 | public string Name { get; set; } 22 | 23 | [XmlElement("options")] 24 | public List Options { get; set; } 25 | } 26 | 27 | [XmlRoot("option")] 28 | public class ServerOption 29 | { 30 | [XmlAttribute("key")] 31 | public string Key { get; set; } 32 | 33 | [XmlAttribute("value")] 34 | public string Value { get; set; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GTGrimServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:43545", 8 | "sslPort": 0 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "launchUrl": "weatherforecast", 16 | "environmentVariables": { 17 | "ASPNETCORE_ENVIRONMENT": "Development" 18 | } 19 | }, 20 | "GTGrimServer": { 21 | "commandName": "Project", 22 | "dotnetRunMessages": "true", 23 | "launchBrowser": true, 24 | "launchUrl": "weatherforecast", 25 | "applicationUrl": "http://localhost:5000", 26 | "environmentVariables": { 27 | "ASPNETCORE_ENVIRONMENT": "Development" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/tv/dev/gb/tv2_gb_s_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2021-04-02T20:20:27+00:00" 5 | 11 6 | 22 7 | 33 8 | 9 | 2021-04-02T20:20:27+00:00 10 | 11 | 12 | 13 | 14 | 0 15 | 0 16 | 1 17 | 18 | 44 19 | 55 20 | 21 | 29 | -------------------------------------------------------------------------------- /GTGrimServer/Utils/DateTimeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Globalization; 6 | 7 | namespace GTGrimServer.Utils 8 | { 9 | public static class DateTimeExtensions 10 | { 11 | private static readonly string[] _formats = new string[] { "yyyy-MM-ddTHH:mm:ssK", "yyyy-MM-ddTHH:mm:ss.ffK", "yyyy-MM-ddTHH:mm:ssZ", "yyyy-MM-ddTHH:mm:ss.ffZ", "yyyy-MM-dd'T'HH:mm:ss.fffzzz" }; 12 | 13 | public static string ToRfc3339String(this DateTime dt) 14 | => dt.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); 15 | 16 | public static DateTime FromRfc3339String(string dtString) 17 | { 18 | DateTime.TryParseExact(dtString, _formats, 19 | CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal, out DateTime dt); // Will be defaulted if incorrect 20 | return dt; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/tv/dev/gb/tv2_gb_l_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 25 | 26 | -------------------------------------------------------------------------------- /GTGrimServer/Utils/XmlSerializerOutputFormatterNamespace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Xml; 6 | using System.Xml.Serialization; 7 | using Microsoft.AspNetCore.Mvc.Formatters; 8 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 9 | 10 | namespace GTGrimServer.Utils 11 | { 12 | public class XmlSerializerOutputFormatterNamespace : XmlSerializerOutputFormatter 13 | { 14 | public XmlSerializerOutputFormatterNamespace(XmlWriterSettings settings) 15 | : base(settings) 16 | { 17 | 18 | } 19 | 20 | protected override void Serialize(XmlSerializer xmlSerializer, XmlWriter xmlWriter, object value) 21 | { 22 | //applying "empty" namespace will produce no namespaces 23 | var emptyNamespaces = new XmlSerializerNamespaces(); 24 | emptyNamespaces.Add("", "any-non-empty-string"); 25 | xmlSerializer.Serialize(xmlWriter, value, emptyNamespaces); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/BbsDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class BbsDTO 9 | { 10 | public int Id { get; set; } 11 | 12 | /// 13 | /// Board ID. Directly linked to the internal user id. User Id 1 = Board Id 1. 14 | /// 15 | public int BbsBoardId { get; set; } 16 | 17 | /// 18 | /// Internal database id of the user who posted the comment. 19 | /// 20 | public int AuthorId { get; set; } 21 | 22 | public string Comment { get; set; } 23 | 24 | public DateTime CreateTime { get; set; } 25 | 26 | public BbsDTO() { } 27 | 28 | public BbsDTO(int boardId, int authorId, string comment, DateTime createTime) 29 | { 30 | BbsBoardId = boardId; 31 | AuthorId = authorId; 32 | Comment = comment; 33 | CreateTime = createTime; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/IDBAccesser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Controllers 7 | { 8 | interface IDBManager 9 | { 10 | /// 11 | /// Adds a new entity to the database. 12 | /// 13 | /// 14 | /// Row ID. 15 | Task AddAsync(T t); 16 | 17 | /// 18 | /// Removes an entity from the database. 19 | /// 20 | /// 21 | Task RemoveAsync(long t); 22 | 23 | /// 24 | /// Gets an entity by ID. 25 | /// 26 | /// 27 | /// 28 | Task GetByIDAsync(long id); 29 | 30 | /// 31 | /// Updates the entity in the database. 32 | /// 33 | /// 34 | Task UpdateAsync(T t); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/PhotoDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class PhotoDTO 9 | { 10 | public long Id { get; set; } 11 | 12 | /// 13 | /// Database Id of the user that triggered this action. 14 | /// 15 | public int UserId { get; set; } 16 | 17 | /// 18 | /// When the activity occured. 19 | /// 20 | public DateTime CreateTime { get; set; } 21 | 22 | public string Comment { get; set; } 23 | public string CarName { get; set; } 24 | public string Place { get; set; } 25 | 26 | public PhotoDTO() { } 27 | 28 | public PhotoDTO(int userId, DateTime createTime, string comment, string carName, string place) 29 | { 30 | UserId = userId; 31 | CreateTime = createTime; 32 | Comment = comment; 33 | CarName = carName; 34 | Place = place; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Nenkai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/UserSpecialDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class UserSpecialDTO 9 | { 10 | /// 11 | /// Row ID 12 | /// 13 | public long Id { get; set; } 14 | 15 | /// 16 | /// Database User ID 17 | /// 18 | public long UserId { get; set; } 19 | 20 | /// 21 | /// Special type? GT6 has it as 3 always 22 | /// 23 | public int Type { get; set; } 24 | 25 | /* CAR_000x 26 | * 1: A gift for your participation in GT Academy 2013 27 | * 2: A gift for your participation in the BMW Z4 Challenge 28 | * 3-7: Entry Bonus: GT5 Final Event 29 | * 8: A gift for your participation in GT Academy 2014 */ 30 | public string Key { get; set; } 31 | 32 | /// 33 | /// If the key is CAR_000x, this is a car label reward 34 | /// 35 | public string Value { get; set; } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /GTGrimServer/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.Linq; 8 | using System.Threading.Tasks; 9 | 10 | using Microsoft.IO; 11 | 12 | namespace GTGrimServer 13 | { 14 | public class Program 15 | { 16 | public static readonly RecyclableMemoryStreamManager StreamManager = new(); 17 | 18 | public static void Main(string[] args) 19 | { 20 | Console.WriteLine("-- GTGrimServer - Open-source Gran Turismo 5 and 6 Server --"); 21 | Console.WriteLine("Init: Starting host"); 22 | 23 | CreateHostBuilder(args).Build().Run(); 24 | } 25 | 26 | public static IHostBuilder CreateHostBuilder(string[] args) => 27 | Host.CreateDefaultBuilder(args) 28 | .ConfigureWebHostDefaults(webBuilder => 29 | { 30 | webBuilder.UseStartup(); 31 | webBuilder.ConfigureKestrel(options => options.Limits.MaxRequestBodySize = 100_000); 32 | }); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /GTGrimServer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31112.23 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GTGrimServer", "GTGrimServer\GTGrimServer.csproj", "{3F2E6A78-977B-4DC4-93CB-F1FF448726FB}" 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 | {3F2E6A78-977B-4DC4-93CB-F1FF448726FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {3F2E6A78-977B-4DC4-93CB-F1FF448726FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {3F2E6A78-977B-4DC4-93CB-F1FF448726FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {3F2E6A78-977B-4DC4-93CB-F1FF448726FB}.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 = {59080D44-56B3-447E-A0D2-4ACF18BE5DD0} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using GTGrimServer.Database.Tables; 7 | 8 | namespace GTGrimServer.Models 9 | { 10 | /// 11 | /// Grim Player. 12 | /// 13 | public class Player 14 | { 15 | /// 16 | /// Database Object. 17 | /// 18 | public UserDTO Data { get; } 19 | 20 | /// 21 | /// Current Session Token. 22 | /// 23 | public SessionToken Token { get; set; } 24 | 25 | /// 26 | /// Last Request Date. 27 | /// 28 | public DateTime LastUpdate { get; set; } 29 | 30 | /// 31 | /// Sets the last update to now. 32 | /// 33 | public void SetLastUpdatedNow() 34 | => LastUpdate = DateTime.Now; 35 | 36 | public override string ToString() 37 | => Data.PSNUserId; 38 | 39 | public Player(UserDTO user, SessionToken token) 40 | { 41 | Data = user; 42 | Token = token; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/SpecialList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | 8 | namespace GTGrimServer.Models.Xml 9 | { 10 | [XmlRoot("user_special_list")] 11 | public class SpecialList 12 | { 13 | [XmlElement("user_special")] 14 | public List Items { get; set; } = new List(); 15 | } 16 | 17 | /// 18 | /// Used by GT6 to award special presents? 19 | /// 20 | [XmlRoot("user_special")] 21 | public class UserSpecial 22 | { 23 | [XmlAttribute("user_id")] 24 | public string UserId { get; set; } 25 | 26 | [XmlAttribute("key")] 27 | public string Key { get; set; } 28 | 29 | [XmlAttribute("value")] 30 | public string Value { get; set; } 31 | 32 | [XmlAttribute("type")] 33 | public int Type { get; set; } 34 | 35 | public UserSpecial() { } 36 | 37 | public UserSpecial(string userId, int type, string key, string value) 38 | { 39 | UserId = userId; 40 | Key = key; 41 | Value = value; 42 | Type = type; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/RegionListController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | 11 | using GTGrimServer.Filters; 12 | using GTGrimServer.Config; 13 | using GTGrimServer.Utils; 14 | 15 | namespace GTGrimServer.Controllers 16 | { 17 | [ApiController] 18 | [PDIClient] 19 | [Route("/init/regionlist.xml")] 20 | [Produces("application/xml")] 21 | public class RegionListController : ControllerBase 22 | { 23 | private readonly ILogger _logger; 24 | private readonly GameServerOptions _gameServerOptions; 25 | 26 | public RegionListController(IOptions options, ILogger logger) 27 | { 28 | _logger = logger; 29 | _gameServerOptions = options.Value; 30 | } 31 | 32 | [HttpGet] 33 | public async Task Get() 34 | { 35 | string regionListFile = "regionlist.xml"; 36 | await this.SendFile(_gameServerOptions.XmlResourcePath, regionListFile); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/PatchController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | 11 | using GTGrimServer.Filters; 12 | using GTGrimServer.Config; 13 | using GTGrimServer.Utils; 14 | 15 | namespace GTGrimServer.Controllers 16 | { 17 | [ApiController] 18 | [Route("/[controller]/")] 19 | [Produces("application/xml")] 20 | [PDIClient] 21 | public class PatchController : ControllerBase 22 | { 23 | private readonly ILogger _logger; 24 | private readonly GameServerOptions _gameServerOptions; 25 | 26 | public PatchController(IOptions options, ILogger logger) 27 | { 28 | _logger = logger; 29 | _gameServerOptions = options.Value; 30 | } 31 | 32 | [HttpGet] 33 | [Route("patchinfo.xml")] 34 | public async Task Get(string region) 35 | { 36 | string serverListFile = "patch/patchinfo.xml"; 37 | await this.SendFile(_gameServerOptions.XmlResourcePath, serverListFile); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /GTGrimServer/Filters/PDIClientAttribute.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Filters; 3 | using Microsoft.Extensions.Primitives; 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | namespace GTGrimServer.Filters 11 | { 12 | /// 13 | /// Verifies that the user agent comes from a proper game client. 14 | /// 15 | /// 16 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] 17 | public class PDIClientAttribute : Attribute, IAsyncActionFilter 18 | { 19 | public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) 20 | { 21 | var userAgent = context.HttpContext.Request.Headers["User-Agent"]; 22 | if (StringValues.IsNullOrEmpty(userAgent)) 23 | { 24 | context.Result = new BadRequestResult(); 25 | return; 26 | } 27 | 28 | if (userAgent.Count != 1 || !userAgent[0].Equals(GTConstants.PDIUserAgent)) 29 | { 30 | context.Result = new UnauthorizedResult(); 31 | return; 32 | } 33 | 34 | await next(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/ServerListController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | 11 | using GTGrimServer.Filters; 12 | using GTGrimServer.Config; 13 | using GTGrimServer.Utils; 14 | 15 | namespace GTGrimServer.Controllers 16 | { 17 | [ApiController] 18 | [Route("/init/{region}/serverlist.xml")] 19 | [Produces("application/xml")] 20 | [PDIClient] 21 | public class ServerListController : ControllerBase 22 | { 23 | private readonly ILogger _logger; 24 | private readonly GameServerOptions _gameServerOptions; 25 | 26 | public ServerListController(IOptions options, ILogger logger) 27 | { 28 | _logger = logger; 29 | _gameServerOptions = options.Value; 30 | } 31 | 32 | [HttpGet] 33 | public async Task Get(string region) 34 | { 35 | string serverListFile = region == "_default" ? "serverlist.xml" : $"{region}/serverlist.xml"; 36 | await this.SendFile(_gameServerOptions.XmlResourcePath, serverListFile); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/ActionLogDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class ActionLogDTO 9 | { 10 | public int Id { get; set; } 11 | 12 | /// 13 | /// Database Id of the user that triggered this action. 14 | /// 15 | public int UserId { get; set; } 16 | 17 | /// 18 | /// When the activity occured. 19 | /// 20 | public DateTime CreateTime { get; set; } 21 | 22 | public string Value1 { get; set; } 23 | public string Value2 { get; set; } 24 | public string Value3 { get; set; } 25 | public string Value4 { get; set; } 26 | public string Value5 { get; set; } 27 | 28 | public ActionLogDTO() { } 29 | 30 | public ActionLogDTO(int userId, DateTime createTime, 31 | string value1 = "", string value2 = "", string value3 = "", string value4 = "", string value5 = "") 32 | { 33 | UserId = userId; 34 | CreateTime = createTime; 35 | Value1 = value1; 36 | Value2 = value2; 37 | Value3 = value3; 38 | Value4 = value4; 39 | Value5 = value5; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/ItemBoxList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot("items")] 12 | public class ItemBoxList 13 | { 14 | [XmlElement("item")] 15 | public List Items { get; set; } 16 | } 17 | 18 | [XmlRoot("item")] 19 | public class ItemBox 20 | { 21 | [XmlAttribute("itembox_id")] 22 | public long ItemBoxId { get; set; } 23 | 24 | [XmlAttribute("type")] 25 | public byte Type { get; set; } 26 | 27 | [XmlAttribute("sender")] 28 | public string Sender { get; set; } 29 | 30 | [XmlAttribute("receiver")] 31 | public byte Receiver { get; set; } 32 | 33 | [XmlAttribute("comment")] 34 | public byte Comment { get; set; } 35 | 36 | [XmlAttribute("create_time")] 37 | public string CreateTimeString { get; set; } 38 | [XmlIgnore] 39 | public DateTime CreateTime 40 | { 41 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 42 | set => CreateTimeString = value.ToRfc3339String(); 43 | } 44 | 45 | [XmlAttribute("stats")] 46 | public byte[] Stats { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/PresenceList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Xml.Serialization; 6 | 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot(ElementName = "presence_list")] 12 | public class PresenceList 13 | { 14 | [XmlElement("user_presence")] 15 | public List Presences { get; set; } 16 | } 17 | 18 | [XmlRoot(ElementName = "user_presence")] 19 | public class Presence 20 | { 21 | [XmlAttribute(AttributeName = "id")] 22 | public long Id { get; set; } 23 | 24 | [XmlAttribute(AttributeName = "stats")] 25 | public string Stats { get; set; } // ENTER COMMUNITY WEB 26 | 27 | [XmlAttribute(AttributeName = "type")] 28 | public int Type { get; set; } 29 | 30 | [XmlAttribute(AttributeName = "update_time")] 31 | public string UpdateTimeString { get; set; } 32 | [XmlIgnore] 33 | public DateTime UpdateTime 34 | { 35 | get => DateTimeExtensions.FromRfc3339String(UpdateTimeString); 36 | set => UpdateTimeString = value.ToRfc3339String(); 37 | } 38 | } 39 | 40 | public enum PresenceState 41 | { 42 | Offline, 43 | OutOfContext, 44 | SameContext, 45 | Error 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/used_car/dev/used_car_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jp 5 | us 6 | gb 7 | fr 8 | de 9 | it 10 | es 11 | pt 12 | nl 13 | ru 14 | pl 15 | tr 16 | el 17 | tw 18 | kr 19 | 200 20 | 2021-04-02T20:20:27+00:00 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/ExtendSecondController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | using System.Xml.Serialization; 11 | using System.IO; 12 | 13 | using GTGrimServer.Sony; 14 | using GTGrimServer.Models; 15 | using GTGrimServer.Models.Xml; 16 | using GTGrimServer.Filters; 17 | 18 | namespace GTGrimServer.Helpers 19 | { 20 | /// 21 | /// Handles requests made by the game to extend the current session. 22 | /// 23 | // Refer to server option 'online.extendsession.interval' for the interval between session extension requests 24 | [ApiController] 25 | [PDIClient] 26 | [Authorize] 27 | [Route("/ap/misc/extend/")] 28 | [Produces("application/xml")] 29 | public class ExtendSessionController : ControllerBase 30 | { 31 | private readonly ILogger _logger; 32 | 33 | public ExtendSessionController(ILogger logger) 34 | { 35 | _logger = logger; 36 | } 37 | 38 | [HttpPost] 39 | public GrimResult Post() 40 | { 41 | _logger.LogInformation($"Got session extend request"); 42 | return GrimResult.FromBool(true); 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/BSpecCarList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Xml.Serialization; 6 | 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot("bspec_car_list")] 12 | public class BSpecCarList 13 | { 14 | [XmlElement("car")] 15 | public List Cars { get; set; } 16 | } 17 | 18 | public class BSpecCar 19 | { 20 | [XmlAttribute("car_id")] 21 | public long CarId { get; set; } 22 | 23 | [XmlAttribute("user_id")] 24 | public string UserId { get; set; } 25 | 26 | [XmlAttribute("car_label")] 27 | public string CarLabel { get; set; } 28 | 29 | [XmlAttribute("car_parameter")] 30 | public byte[] CarParameter { get; set; } 31 | 32 | [XmlAttribute("status")] 33 | public int Status { get; set; } 34 | 35 | [XmlAttribute("lock_time")] 36 | public string LockTimeString { get; set; } 37 | [XmlIgnore] 38 | public DateTime LockTime 39 | { 40 | get => DateTimeExtensions.FromRfc3339String(LockTimeString); 41 | set => LockTimeString = value.ToRfc3339String(); 42 | } 43 | 44 | [XmlAttribute("lock_user_id")] 45 | public string LockUserId { get; set; } 46 | 47 | [XmlAttribute("thumbnail_photo_id")] 48 | public string ThumbnailPhotoId { get; set; } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/UsedCarDealershipController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using Microsoft.AspNetCore.Authorization; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.IO; 11 | 12 | using GTGrimServer.Filters; 13 | using GTGrimServer.Config; 14 | using GTGrimServer.Utils; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | /// 19 | /// Provides news to the player. 20 | /// 21 | [ApiController] 22 | [Authorize] 23 | [PDIClient] 24 | [Route("/used_car/")] 25 | [Produces("application/xml")] 26 | public class UsedCarDealershipController : ControllerBase 27 | { 28 | private readonly ILogger _logger; 29 | private readonly GameServerOptions _gameServerOptions; 30 | 31 | public UsedCarDealershipController(IOptions options, ILogger logger) 32 | { 33 | _logger = logger; 34 | _gameServerOptions = options.Value; 35 | } 36 | 37 | [HttpGet] 38 | [Route("{server}/used_car_list.xml")] 39 | public async Task Get(string server) 40 | { 41 | string usedCarListFile = $"used_car/{server}/used_car_list.xml"; 42 | await this.SendFile(_gameServerOptions.XmlResourcePath, usedCarListFile); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/SimpleFriendList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | 8 | namespace GTGrimServer.Models.Xml 9 | { 10 | /// 11 | /// Basic list of friends. 12 | /// 13 | [XmlRoot("friends")] 14 | public class SimpleFriendList 15 | { 16 | [XmlElement("profile")] 17 | public List Items { get; set; } = new List(); 18 | } 19 | 20 | /// 21 | /// Simple friend profile. 22 | /// 23 | [XmlRoot("profile")] 24 | public class SimpleFriend 25 | { 26 | /// 27 | /// PSN User Id of the friend. 28 | /// 29 | [XmlAttribute("id")] 30 | public string UserId { get; set; } 31 | 32 | /// 33 | /// A-Spec Level of the friend (GT5 only). 34 | /// 35 | [XmlAttribute("aspec_level")] 36 | public int ASpecLevel { get; set; } 37 | 38 | /// 39 | /// B-Spec Level of the friend (GT5 only). 40 | /// 41 | [XmlAttribute("bspec_level")] 42 | public int BSpecLevel { get; set; } 43 | 44 | public SimpleFriend() { } 45 | 46 | public SimpleFriend(string userId, int aspecLevel, int bspecLevel) 47 | { 48 | UserId = userId; 49 | ASpecLevel = aspecLevel; 50 | BSpecLevel = bspecLevel; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/RankingList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Xml.Serialization; 6 | 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot("ranking_list")] 12 | public class RankingList 13 | { 14 | [XmlElement("ranking")] 15 | public List Rankings { get; set; } 16 | } 17 | 18 | [XmlRoot("ranking")] 19 | public class Ranking 20 | { 21 | [XmlAttribute("board_id")] 22 | public long BoardId { get; set; } 23 | 24 | [XmlAttribute("score")] 25 | public int Score { get; set; } 26 | 27 | [XmlAttribute("replay_id")] 28 | public int ReplayId { get; set; } 29 | 30 | [XmlAttribute("rank")] 31 | public int Rank { get; set; } 32 | 33 | [XmlAttribute("src_id")] 34 | public int SourceId { get; set; } 35 | 36 | [XmlAttribute("user_id")] 37 | public string UserId { get; set; } 38 | 39 | [XmlAttribute("create_time")] 40 | public string CreateTimeString { get; set; } 41 | [XmlIgnore] 42 | public DateTime CreateTime 43 | { 44 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 45 | set => CreateTimeString = value.ToRfc3339String(); 46 | } 47 | 48 | [XmlAttribute("stats")] 49 | public byte[] Stats { get; set; } 50 | 51 | [XmlAttribute("nickname")] 52 | public string Nickname { get; set; } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/TicketResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml; 7 | using System.Xml.Serialization; 8 | using GTGrimServer.Utils; 9 | 10 | namespace GTGrimServer.Models.Xml 11 | { 12 | /// 13 | /// Represents a NP Login Ticket result. 14 | /// 15 | [XmlRoot("grim")] 16 | public class TicketResult 17 | { 18 | /// 19 | /// Whether it succeeded. 20 | /// 21 | [XmlElement("result")] 22 | public string Result { get; set; } 23 | 24 | /// 25 | /// PSN User ID. 26 | /// 27 | [XmlElement("user_id")] 28 | public string UserId { get; set; } 29 | 30 | /// 31 | /// Nickname. 32 | /// 33 | [XmlElement("nickname")] 34 | public string Nickname { get; set; } 35 | 36 | /// 37 | /// Grim User Id. 38 | /// 39 | [XmlElement("user_no")] 40 | public int UserNumber { get; set; } 41 | 42 | /// 43 | /// Server Time. 44 | /// 45 | [XmlElement("server_time")] 46 | public string ServerTimeString { get; set; } 47 | [XmlIgnore] 48 | public DateTime ServerTime 49 | { 50 | get => DateTimeExtensions.FromRfc3339String(ServerTimeString); 51 | set => ServerTimeString = value.ToRfc3339String(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/CourseDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class CourseDTO 9 | { 10 | /// 11 | /// Row Id 12 | /// 13 | public long Id { get; set; } 14 | 15 | /// 16 | /// Internal Database Id of the main user. 17 | /// 18 | public long UserId { get; set; } 19 | 20 | /// 21 | /// Internal Database Id of the friend. 22 | /// 23 | public long FriendId { get; set; } 24 | 25 | public DateTime CreateTime { get; set; } 26 | 27 | public DateTime UpdateTime { get; set; } 28 | 29 | public int Status { get; set; } 30 | 31 | public long PhotoId { get; set; } 32 | 33 | public string Title { get; set; } 34 | 35 | public string Comment { get; set; } 36 | 37 | public int TitleHidden { get; set; } 38 | 39 | public int CommentHidden { get; set; } 40 | 41 | public int PhotoHidden { get; set; } 42 | 43 | public string Theme { get; set; } 44 | 45 | public int Length { get; set; } 46 | 47 | public int OneWay { get; set; } 48 | 49 | /// 50 | /// Internal database id of the source user who created this track. 51 | /// 52 | public long SourceUserId { get; set; } 53 | 54 | public int Straight { get; set; } 55 | 56 | public int Height { get; set; } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/MailList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Xml.Serialization; 6 | 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot(ElementName = "gtmail_list")] 12 | public class MailList 13 | { 14 | public List Mails { get; set; } 15 | } 16 | 17 | [XmlRoot(ElementName = "gtmail")] 18 | public class Mail 19 | { 20 | [XmlAttribute(AttributeName = "mail_id")] 21 | public long MailId { get; set; } 22 | 23 | [XmlAttribute(AttributeName = "from")] 24 | public string FromUsername { get; set; } 25 | 26 | [XmlAttribute(AttributeName = "to")] 27 | public string ToUsername { get; set; } 28 | 29 | [XmlAttribute(AttributeName = "from_nickname")] 30 | public string FromNickname { get; set; } 31 | 32 | [XmlAttribute(AttributeName = "to_nickname")] 33 | public string ToNickname { get; set; } 34 | 35 | [XmlAttribute(AttributeName = "subject")] 36 | public string Subject { get; set; } 37 | 38 | [XmlAttribute(AttributeName = "body")] 39 | public string Body { get; set; } 40 | 41 | [XmlAttribute(AttributeName = "create_time")] 42 | public string CreateTimeString { get; set; } 43 | [XmlIgnore] 44 | public DateTime CreateTime 45 | { 46 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 47 | set => CreateTimeString = value.ToRfc3339String(); 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /GTGrimServer/GTGrimServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | GTGrimServer 6 | GTGrimServer 7 | dad7dca7-d08d-49d3-ae30-7442c6550f5c 8 | 9 | 10 | 11 | 12 | Always 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | Always 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/GrimControllerBase.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Mvc.Infrastructure; 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | 9 | using System.Xml.Serialization; 10 | using System.IO; 11 | 12 | using GTGrimServer.Models; 13 | using GTGrimServer.Results; 14 | using GTGrimServer.Services; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | public class GrimControllerBase : ControllerBase 19 | { 20 | protected readonly PlayerManager Players; 21 | 22 | public GrimControllerBase(PlayerManager players) 23 | { 24 | Players = players; 25 | } 26 | 27 | /// 28 | /// Current player for this request. 29 | /// 30 | public Player Player 31 | { 32 | get 33 | { 34 | string token = Request.Cookies["X-gt-token"]; 35 | if (token is null) 36 | return null; 37 | 38 | Player player = Players.GetPlayerByToken(token); 39 | 40 | // TODO: Check if expired 41 | 42 | return player; 43 | } 44 | } 45 | 46 | private BanObjectResult Ban([ActionResultObjectValue] object value) 47 | => new BanObjectResult(value); 48 | 49 | private ConsoleBanObjectResult ConsoleBan([ActionResultObjectValue] object value) 50 | => new ConsoleBanObjectResult(value); 51 | 52 | private SuspendedObjectResult Suspended([ActionResultObjectValue] object value) 53 | => new SuspendedObjectResult(value); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /GTGrimServer/Services/PlayerManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Collections.Concurrent; 6 | 7 | using GTGrimServer.Models; 8 | using GTGrimServer.Database.Tables; 9 | 10 | namespace GTGrimServer.Services 11 | { 12 | /// 13 | /// Handles all the currently connected players to the server. 14 | /// 15 | public class PlayerManager 16 | { 17 | public ConcurrentDictionary Players { get; set; } = new(); 18 | 19 | public PlayerManager() 20 | { 21 | 22 | } 23 | 24 | public bool RemoveByToken(SessionToken sToken) 25 | => Players.TryRemove(sToken.Token, out _); 26 | 27 | public bool AddByToken(SessionToken sToken, Player player) 28 | => Players.TryAdd(sToken.Token, player); 29 | 30 | public bool RemovePlayer(Player player) 31 | => Players.TryRemove(player.Token.Token, out _); 32 | 33 | public bool AddUser(Player player) 34 | => Players.TryAdd(player.Token.Token, player); 35 | 36 | public Player GetPlayerByToken(string sToken) 37 | { 38 | if (Players.TryGetValue(sToken, out Player player)) 39 | return player; 40 | 41 | return null; // TODO Handle 42 | } 43 | 44 | public void UpdatePlayerToken(Player player, SessionToken newToken) 45 | { 46 | if (Players.TryRemove(player.Token.Token, out _)) 47 | { 48 | player.Token = newToken; 49 | Players.TryAdd(player.Token.Token, player); 50 | } 51 | } 52 | 53 | public Player GetPlayerByName(string name) 54 | => Players.FirstOrDefault(e => e.Value.Data.PSNUserId.Equals(name)).Value; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/GreetingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using Microsoft.AspNetCore.Authorization; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.IO; 11 | 12 | using GTGrimServer.Filters; 13 | using GTGrimServer.Config; 14 | using GTGrimServer.Utils; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | /// 19 | /// Provides a greeting card on logon to the player if provided. (GT5 only). 20 | /// 21 | [ApiController] 22 | [Authorize] 23 | [PDIClient] 24 | [Route("/[controller]/")] 25 | [Produces("application/xml")] 26 | public class GreetingController : ControllerBase 27 | { 28 | private readonly ILogger _logger; 29 | private readonly GameServerOptions _gameServerOptions; 30 | 31 | public GreetingController(IOptions options, ILogger logger) 32 | { 33 | _logger = logger; 34 | _gameServerOptions = options.Value; 35 | } 36 | 37 | [HttpGet] 38 | [Route("{server}/{region}/greeting_list.xml")] 39 | public async Task GetGreetingList(string server, string region) 40 | { 41 | string greetingListFile = $"greeting/{server}/{region}/greeting_list.xml"; 42 | await this.SendFile(_gameServerOptions.XmlResourcePath, greetingListFile); 43 | } 44 | 45 | [HttpGet] 46 | [Route("{server}/{region}/{imageName}.img")] 47 | public async Task GetGreetingCardImage(string server, string region, string imageName) 48 | { 49 | string greetingImageFile = $"greeting/{server}/{region}/{imageName}.img"; 50 | await this.SendFile(_gameServerOptions.XmlResourcePath, greetingImageFile); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/PhotoList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | /// 12 | /// Represents a list of photo information. 13 | /// 14 | [XmlRoot("photos")] 15 | public class PhotoList 16 | { 17 | [XmlElement("photo")] 18 | public List Photos { get; set; } = new(); 19 | } 20 | 21 | [XmlRoot("photo")] 22 | public class Photo 23 | { 24 | /// 25 | /// Database Id 26 | /// 27 | [XmlAttribute("photo_id")] 28 | public long PhotoId { get; set; } 29 | 30 | /// 31 | /// Location of the photo. 32 | /// 33 | [XmlAttribute("place")] 34 | public string Place { get; set; } 35 | 36 | /// 37 | /// Car name of car inside the photo. 38 | /// 39 | [XmlAttribute("car_name")] 40 | public string CarName { get; set; } 41 | 42 | /// 43 | /// User Id of the creator of the photo. 44 | /// 45 | [XmlAttribute("user_id")] 46 | public string UserId { get; set; } 47 | 48 | /// 49 | /// Comment of this photo. (140 chars max) 50 | /// 51 | [XmlAttribute("comment")] 52 | public string Comment { get; set; } 53 | 54 | /// 55 | /// Creation date of this photo. 56 | /// 57 | [XmlAttribute("create_time")] 58 | public string CreateTimeString { get; set; } 59 | [XmlIgnore] 60 | public DateTime CreateTime 61 | { 62 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 63 | set => CreateTimeString = value.ToRfc3339String(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/BbsCommentList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | /// 12 | /// Represents a GT5 board's comment list. 13 | /// 14 | [XmlRoot("bbs_comment_list")] 15 | public class BbsCommentList 16 | { 17 | [XmlElement("bbs_comment")] 18 | public List Comments { get; set; } = new(); 19 | } 20 | 21 | public class BbsComment 22 | { 23 | /// 24 | /// Comment Id. 25 | /// 26 | [XmlAttribute("bbs_comment_id")] 27 | public long CommentId { get; set; } 28 | 29 | /// 30 | /// Board Id that this comment belongs to. 31 | /// 32 | [XmlAttribute("bbs_board_id")] 33 | public long BoardId { get; set; } 34 | 35 | /// 36 | /// Author User Id of this comment. 37 | /// 38 | [XmlAttribute("user_id")] 39 | public string UserId { get; set; } 40 | 41 | /// 42 | /// Contents of this comment. 43 | /// 44 | [XmlAttribute("comment")] 45 | public string Comment { get; set; } 46 | 47 | /// 48 | /// Nickname of the author of this comment. 49 | /// 50 | [XmlAttribute("nickname")] 51 | public string Nickname { get; set; } 52 | 53 | /// 54 | /// Creation date of this comment. 55 | /// 56 | [XmlAttribute("create_time")] 57 | public string CreateTimeString { get; set; } 58 | [XmlIgnore] 59 | public DateTime CreateTime 60 | { 61 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 62 | set => CreateTimeString = value.ToRfc3339String(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/GrimResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Globalization; 6 | 7 | using System.Xml.Serialization; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot("grim")] 12 | public class GrimResult 13 | { 14 | [XmlElement("result")] 15 | public string Result { get; set; } 16 | 17 | public GrimResult() { } 18 | 19 | public GrimResult(string result) 20 | => Result = result; 21 | 22 | public static GrimResult FromString(string result) 23 | => new(result); 24 | 25 | public static GrimResult FromBool(bool result) 26 | => new(result ? "1" : "0"); 27 | 28 | public static GrimResult FromByte(byte result) 29 | => new(result.ToString()); 30 | 31 | public static GrimResult FromSByte(sbyte result) 32 | => new(result.ToString()); 33 | 34 | public static GrimResult FromShort(short result) 35 | => new(result.ToString()); 36 | 37 | public static GrimResult FromUShort(ushort result) 38 | => new(result.ToString()); 39 | 40 | public static GrimResult FromInt(int result) 41 | => new(result.ToString()); 42 | 43 | public static GrimResult FromUInt(uint result) 44 | => new(result.ToString()); 45 | 46 | public static GrimResult FromLong(long result) 47 | => new(result.ToString()); 48 | 49 | public static GrimResult FromULong(ulong result) 50 | => new(result.ToString()); 51 | 52 | public static GrimResult FromSingle(float result) 53 | => new(result.ToString()); 54 | 55 | public static GrimResult FromDouble(double result) 56 | => new(result.ToString()); 57 | 58 | public static GrimResult FromDateTimeRfc3339(DateTime result) 59 | => new(result.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo)); // Rfc3339 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/ItemBoxController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Config; 16 | using GTGrimServer.Utils; 17 | using GTGrimServer.Models; 18 | using GTGrimServer.Models.Xml; 19 | using GTGrimServer.Filters; 20 | 21 | namespace GTGrimServer.Controllers 22 | { 23 | /// 24 | /// Provides news to the player. 25 | /// 26 | [ApiController] 27 | [PDIClient] 28 | [Authorize] 29 | [Route("/ap/[controller]")] 30 | [Produces("application/xml")] 31 | public class ItemBoxController : ControllerBase 32 | { 33 | private readonly ILogger _logger; 34 | private readonly GameServerOptions _gsOptions; 35 | 36 | public ItemBoxController(IOptions options, ILogger logger) 37 | { 38 | _logger = logger; 39 | _gsOptions = options.Value; 40 | } 41 | 42 | [HttpPost] 43 | public async Task Post() 44 | { 45 | GrimRequest requestReq = await GrimRequest.Deserialize(Request.Body); 46 | if (requestReq is null) 47 | return BadRequest(); 48 | 49 | switch (requestReq.Command) 50 | { 51 | case "itembox.getlist": 52 | return OnGetItemList(requestReq); 53 | } 54 | 55 | _logger.LogDebug("Received unimplemented itembox command: {command}", requestReq.Command); 56 | 57 | return BadRequest(); 58 | } 59 | 60 | public ActionResult OnGetItemList(GrimRequest request) 61 | { 62 | var result = new ItemBoxList(); 63 | return Ok(result); 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /GTGrimServer/Resources/data2/tv/dev/gb/tv2_gb_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 1 6 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 44 | 45 | 70 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/LogController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | using System.Xml.Serialization; 11 | using System.IO; 12 | 13 | using GTGrimServer.Controllers; 14 | using GTGrimServer.Services; 15 | using GTGrimServer.Sony; 16 | using GTGrimServer.Filters; 17 | 18 | namespace GTGrimServer.Helpers 19 | { 20 | /// 21 | /// Handles logging made by the game for the server to keep track of what the player is doing. 22 | /// 23 | [ApiController] 24 | [PDIClient] 25 | [Authorize] 26 | [Route("/log/{server}/")] 27 | [Produces("application/xml")] 28 | public class LogController : GrimControllerBase 29 | { 30 | private readonly ILogger _logger; 31 | 32 | public LogController(PlayerManager playerManager, ILogger logger) 33 | : base(playerManager) 34 | { 35 | _logger = logger; 36 | } 37 | 38 | [HttpGet] 39 | public ActionResult Get(string server) 40 | { 41 | if (!Request.Headers.TryGetValue("X-gt-log", out var hVal)) 42 | return BadRequest(); 43 | 44 | if (Player is null) 45 | { 46 | _logger.LogWarning("Could not get current player"); 47 | return BadRequest(); 48 | } 49 | 50 | string argStr = hVal.FirstOrDefault(); 51 | if (argStr.StartsWith("ADHOC")) 52 | { 53 | _logger.LogInformation("[{name}] received ADHOC crash: {argStr}", Player.Data.PSNUserId, argStr); 54 | return Ok(); 55 | } 56 | 57 | string[] args = argStr.Split(':'); 58 | LogHelper.Humanify(args); 59 | 60 | string humanified = string.Join(':', args); 61 | _logger.LogInformation("[{name}] Log: {args}", Player.Data.PSNUserId, humanified); 62 | 63 | return Ok(); 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/news/dev/gb/500.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 500 4 | 5 | 6 | 0 7 | 0 8 | 0 9 | 10 | 11 | Breaking News 12 | 13 | Not clickbait 14 | 15 | 16 | Du 17 | Du hast 18 | Du hast mich 19 | Du hast mich 20 | Du hast mich gefragt 21 | Du hast mich gefragt 22 | Du hast mich gefragt und ich hab' nichts gesagt 23 | Willst du bis der Tod euch scheidet 24 | Treu ihr sein für alle Tage 25 | Nein 26 | Nein 27 | 28 | 29 | 5 30 | 31 | 2021-04-02T20:20:27+00:00 32 | 2021-04-02T20:20:27+00:00 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Game Rating Vote Results 47 | The game rating results are in! Here are the best games according to you: 48 | 49 | Ranking##Game##Comment##Rating$$**1st##**GT6##**It's just good##**SS$$2nd##GTPSP##Meh##B$$3rd##GT5##Bad##F 50 | 51 | 52 | 53 | Page switching test 54 | Hi 55 | AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA 56 | $$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA 57 | $$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA 58 | $$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$ 59 | $$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$ 60 | $$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$AAAAAAAAAAAAAAAAAAAA$$ 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Data/MuseumController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.IO; 11 | 12 | using GTGrimServer.Filters; 13 | using GTGrimServer.Config; 14 | using GTGrimServer.Utils; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | [ApiController] 19 | [Authorize] 20 | [Route("/")] 21 | [PDIClient] 22 | [Produces("application/xml")] 23 | public class MuseumController : ControllerBase 24 | { 25 | private readonly ILogger _logger; 26 | private readonly GameServerOptions _gameServerOptions; 27 | 28 | public MuseumController(IOptions options, ILogger logger) 29 | { 30 | _logger = logger; 31 | _gameServerOptions = options.Value; 32 | } 33 | 34 | [HttpGet] 35 | [Route("/data2/[controller]/{server}/{region}/museum_{fileRegion}_{arg}.xml")] 36 | public async Task GetMuseum(string server, string region, string fileRegion, string arg) 37 | { 38 | if (arg.StartsWith("l_") && arg.Length > 2 && int.TryParse(arg.AsSpan(2), out int listId)) 39 | await GetMuseumList(server, region, fileRegion, listId); 40 | else if (arg.Length > 0 && int.TryParse(arg, out int itemId)) 41 | await GetMuseumItem(server, region, fileRegion, itemId); 42 | else 43 | { 44 | // Handle issue 45 | } 46 | 47 | } 48 | 49 | [HttpGet] 50 | [Route("/data/[controller]/{server}/{region}/museum_{fileRegion}_{imageId:int}.img")] 51 | public void GetMuseumImage(string server, string region, string fileRegion, int imageId) 52 | { 53 | return; 54 | } 55 | 56 | private async Task GetMuseumList(string server, string region, string fileRegion, int listId) 57 | { 58 | string museumListFile = $"data2/museum/{server}/{region}/museum_{fileRegion}_l_{listId}.xml"; 59 | await this.SendFile(_gameServerOptions.XmlResourcePath, museumListFile); 60 | } 61 | 62 | public async Task GetMuseumItem(string server, string region, string fileRegion, int itemId) 63 | { 64 | string museumItemFile = $"data2/museum/{server}/{region}/museum_{fileRegion}_{itemId}.xml"; 65 | await this.SendFile(_gameServerOptions.XmlResourcePath, museumItemFile); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/LocaleController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.Extensions.Options; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Threading.Tasks; 9 | 10 | using System.Xml.Serialization; 11 | using System.IO; 12 | 13 | using GTGrimServer.Utils; 14 | using GTGrimServer.Sony; 15 | using GTGrimServer.Models.Xml; 16 | using GTGrimServer.Config; 17 | using GTGrimServer.Filters; 18 | 19 | namespace GTGrimServer.Helpers 20 | { 21 | /// 22 | /// Handles logging made by the game for the server to keep track of what the player is doing. 23 | /// 24 | [ApiController] 25 | [PDIClient] 26 | [Route("/ap/locale")] 27 | [Produces("application/xml")] 28 | public class LocaleController : ControllerBase 29 | { 30 | private readonly ILogger _logger; 31 | private readonly GameServerOptions _gsOptions; 32 | 33 | public LocaleController(IOptions options, ILogger logger) 34 | { 35 | _logger = logger; 36 | _gsOptions = options.Value; 37 | } 38 | 39 | [HttpPost] 40 | public async Task Post() 41 | { 42 | GrimRequest requestReq = await GrimRequest.Deserialize(Request.Body); 43 | if (requestReq is null) 44 | return BadRequest(); 45 | 46 | switch (requestReq.Command) 47 | { 48 | case "servertime.get": 49 | return OnGetServerTime(requestReq); 50 | case "language.set": 51 | return OnSetLanguage(requestReq); 52 | } 53 | 54 | _logger.LogDebug("Received unimplemented locale command: {command}", requestReq.Command); 55 | 56 | return BadRequest(); 57 | } 58 | 59 | private ActionResult OnGetServerTime(GrimRequest gRequest) 60 | { 61 | if (_gsOptions.GameType != GameType.GT5) 62 | { 63 | _logger.LogWarning("Got servertime.get request for non GT5"); 64 | return BadRequest(); 65 | } 66 | 67 | var result = GrimResult.FromDateTimeRfc3339(DateTime.Now); 68 | return Ok(result); 69 | } 70 | 71 | private ActionResult OnSetLanguage(GrimRequest gRequest) 72 | { 73 | if (!gRequest.TryGetParameterByKey("language", out GrimRequestParam param)) 74 | { 75 | _logger.LogWarning("Got missing language parameter for language.set"); 76 | return BadRequest(); 77 | } 78 | 79 | return Ok(GrimResult.FromString(param.Text.ToLower())); 80 | } 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/BbsBoardDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | using System.Data; 8 | 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class BbsBoardDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public BbsBoardDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM bbs WHERE id = @id", new { Id = id }); 31 | 32 | public async Task UpdateAsync(BbsDTO pData) 33 | => await _con.ExecuteAsync(@"UPDATE bbs", pData); 34 | 35 | /// 36 | /// Gets all the comments in a board. 37 | /// 38 | /// Database Id of the board. 39 | /// Bbs object list. 40 | public async Task> GetAllCommentsOfBoard(int boardId) 41 | => await _con.QueryAsync(@"SELECT * FROM bbs WHERE bbs_board_id=@Id", new { Id = boardId }); 42 | 43 | public async Task AddAsync(BbsDTO bbs) 44 | { 45 | var query = 46 | @"INSERT INTO bbs (bbs_board_id, author_id, comment, create_time) 47 | VALUES(@BbsBoardId, @AuthorId, @Comment, @CreateTime) 48 | returning id"; 49 | 50 | return await _con.ExecuteScalarAsync(query, new { bbs.BbsBoardId, bbs.AuthorId, bbs.Comment, bbs.CreateTime }); 51 | } 52 | 53 | public async Task RemoveAsync(long id) 54 | => await _con.ExecuteAsync(@"DELETE FROM bbs WHERE id=@Id", new { Id = id }); 55 | 56 | public async Task RemoveByBoardIdAsync(int boardId) 57 | => await _con.ExecuteAsync(@"DELETE FROM bbs WHERE bbs_board_id=@Id", new { Id = boardId }); 58 | 59 | public void CreateTable() 60 | { 61 | string query = 62 | @"CREATE TABLE IF NOT EXISTS bbs ( 63 | id SERIAL PRIMARY KEY, 64 | bbs_board_id INTEGER REFERENCES users(id), 65 | 66 | author_id INTEGER REFERENCES users(id), 67 | comment TEXT, 68 | create_time TIMESTAMP WITHOUT TIME ZONE 69 | );"; 70 | 71 | _con.Execute(query); 72 | 73 | string query2 = @"CREATE INDEX IF NOT EXISTS bbs_bbsboardid_idx ON bbs (bbs_board_id)"; 74 | _con.Execute(query2); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/ActionLogDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | using System.Data; 8 | 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class ActionLogDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public ActionLogDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM actionlogs WHERE id = @id", new { Id = id }); 31 | 32 | public async Task UpdateAsync(ActionLogDTO pData) 33 | => await _con.ExecuteAsync(@"UPDATE actionlogs", pData); 34 | 35 | /// 36 | /// Gets all the actions of an user. 37 | /// 38 | /// Database Id of the user. 39 | /// Action object list. 40 | public async Task> GetAllActionsOfUser(int userId) 41 | => await _con.QueryAsync(@"SELECT * FROM actionlogs WHERE user_id=@Id", new { Id = userId }); 42 | 43 | public async Task AddAsync(ActionLogDTO log) 44 | { 45 | var query = 46 | @"INSERT INTO actionlogs (user_id, create_time, value1, value2, value3, value4, value5) 47 | VALUES(@UserId, @CreateTime, @Value1, @Value2, @Value3, @Value4, @Value5) 48 | returning id"; 49 | 50 | return await _con.ExecuteScalarAsync(query, new { log.UserId, log.CreateTime, log.Value1, log.Value2, log.Value3, log.Value4, log.Value5 }); 51 | } 52 | 53 | public async Task RemoveAsync(long id) 54 | => await _con.ExecuteAsync(@"DELETE FROM actionlogs WHERE id=@Id", new { Id = id }); 55 | 56 | 57 | public void CreateTable() 58 | { 59 | string query = 60 | @"CREATE TABLE IF NOT EXISTS actionlogs ( 61 | id SERIAL PRIMARY KEY, 62 | user_id INTEGER REFERENCES users(id), 63 | 64 | create_time TIMESTAMP WITHOUT TIME ZONE, 65 | value1 TEXT, 66 | value2 TEXT, 67 | value3 TEXT, 68 | value4 TEXT, 69 | value5 TEXT 70 | );"; 71 | 72 | _con.Execute(query); 73 | 74 | string query2 = @"CREATE INDEX IF NOT EXISTS actionlogs_user_id_idx ON actionlogs (user_id)"; 75 | _con.Execute(query2); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/BSpecController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Config; 16 | using GTGrimServer.Services; 17 | using GTGrimServer.Models; 18 | using GTGrimServer.Models.Xml; 19 | using GTGrimServer.Filters; 20 | 21 | namespace GTGrimServer.Controllers 22 | { 23 | /// 24 | /// Remote B-Spec handler. 25 | /// 26 | [ApiController] 27 | [PDIClient] 28 | [Authorize] 29 | [Route("/ap/[controller]")] 30 | [Produces("application/xml")] 31 | public class BSpecController : GrimControllerBase 32 | { 33 | private readonly ILogger _logger; 34 | private readonly GameServerOptions _gsOptions; 35 | 36 | public BSpecController(PlayerManager players, IOptions options, ILogger logger) 37 | : base(players) 38 | { 39 | _logger = logger; 40 | _gsOptions = options.Value; 41 | } 42 | 43 | [HttpPost] 44 | public async Task Post() 45 | { 46 | var player = Player; 47 | if (player is null) 48 | { 49 | _logger.LogWarning("Could not get current player for host {host}", Request.Host); 50 | return Unauthorized(); 51 | } 52 | 53 | if (_gsOptions.GameType != GameType.GT5) 54 | { 55 | _logger.LogWarning("Got a bspec remote request on a GT6 server from host: {host}", Request.Host); 56 | return Unauthorized(); 57 | } 58 | 59 | GrimRequest gRequest = await GrimRequest.Deserialize(Request.Body); 60 | if (gRequest is null) 61 | { 62 | // Handle 63 | var badReq = GrimResult.FromInt(-1); 64 | return BadRequest(badReq); 65 | } 66 | 67 | _logger.LogDebug("<- {command}", gRequest.Command); 68 | 69 | switch (gRequest.Command) 70 | { 71 | case "bspec.carlist": 72 | return await OnRequestCarList(player, gRequest); 73 | } 74 | 75 | _logger.LogDebug("Received unimplemented bspec call: {command}", gRequest.Command); 76 | var res = GrimResult.FromInt(-1); 77 | return BadRequest(res); 78 | } 79 | 80 | private async Task OnRequestCarList(Player player, GrimRequest request) 81 | { 82 | var result = new ItemBoxList(); 83 | return Ok(result); 84 | } 85 | } 86 | } -------------------------------------------------------------------------------- /GTGrimServer/Controllers/EventController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.Options; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Threading.Tasks; 11 | 12 | using System.Xml.Serialization; 13 | using System.IO; 14 | 15 | using GTGrimServer.Filters; 16 | using GTGrimServer.Sony; 17 | using GTGrimServer.Utils; 18 | using GTGrimServer.Config; 19 | 20 | namespace GTGrimServer.Helpers 21 | { 22 | /// 23 | /// Handles logging made by the game for the server to keep track of what the player is doing. 24 | /// 25 | [ApiController] 26 | [PDIClient] 27 | [Authorize] 28 | [Route("/[controller]")] 29 | [Produces("application/xml")] 30 | public class EventController : ControllerBase 31 | { 32 | private readonly ILogger _logger; 33 | private readonly GameServerOptions _gameServerOptions; 34 | 35 | public EventController(IOptions options, ILogger logger) 36 | { 37 | _logger = logger; 38 | _gameServerOptions = options.Value; 39 | } 40 | 41 | [HttpGet] 42 | [Route("{server}/{fileName}")] 43 | public async Task GetImageFile(string server, string fileName) 44 | { 45 | 46 | if (!fileName.EndsWith(".png") && !fileName.EndsWith(".img")) 47 | { 48 | Response.StatusCode = StatusCodes.Status404NotFound; 49 | return; 50 | } 51 | 52 | string eventListFile = $"event/{server}/{fileName}"; 53 | await this.SendFile(_gameServerOptions.XmlResourcePath, eventListFile); 54 | } 55 | 56 | [HttpGet] 57 | [Route("{server}/setting.xml")] 58 | public async Task GetSettings(string server) 59 | { 60 | string settingsFile = $"event/{server}/setting.xml"; 61 | await this.SendFile(_gameServerOptions.XmlResourcePath, settingsFile); 62 | } 63 | 64 | 65 | [HttpGet] 66 | [Route("{server}/event_list.xml")] 67 | public async Task GetOnlineEventList(string server) 68 | { 69 | string eventListFile; 70 | if (_gameServerOptions.GameType == GameType.GT5) 71 | eventListFile = $"event/{server}/event_list_gt5.xml"; 72 | else 73 | eventListFile = $"event/{server}/event_list.xml"; 74 | 75 | await this.SendFile(_gameServerOptions.XmlResourcePath, eventListFile); 76 | 77 | } 78 | 79 | [HttpGet] 80 | [Route("{server}/event_{folderId:int}.xml")] 81 | public async Task GetOnlineEvent(string server, int folderId) 82 | { 83 | string eventFile = $"event/{server}/event_{folderId}.xml"; 84 | await this.SendFile(_gameServerOptions.XmlResourcePath, eventFile); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/NewsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.AspNetCore.Authorization; 5 | using Microsoft.Extensions.Options; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.IO; 11 | 12 | using GTGrimServer.Config; 13 | using GTGrimServer.Utils; 14 | using GTGrimServer.Filters; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | /// 19 | /// Provides news to the player. 20 | /// 21 | [ApiController] 22 | [PDIClient] 23 | [Authorize] 24 | [Route("/[controller]")] 25 | [Produces("application/xml")] 26 | public class NewsController : ControllerBase 27 | { 28 | private readonly ILogger _logger; 29 | private readonly GameServerOptions _gameServerOptions; 30 | 31 | public NewsController(IOptions options, ILogger logger) 32 | { 33 | _logger = logger; 34 | _gameServerOptions = options.Value; 35 | } 36 | 37 | [HttpGet] 38 | [Route("{serverId}/{region}/{fileId:int}.xml")] 39 | public async Task GetNews(string serverId, string region, int fileId) 40 | { 41 | string newsFile = $"news/{serverId}/{region}/{fileId}.xml"; 42 | await this.SendFile(_gameServerOptions.XmlResourcePath, newsFile); 43 | } 44 | 45 | [HttpGet] 46 | [Route("{serverId}/{region}/l{category_id1:int}_{category_id2:int}.xml")] 47 | public async Task GetNewsList(string serverId, string region, int category_id1, int category_id2) 48 | { 49 | string newsListFile = $"news/{serverId}/{region}/l{category_id1}_{category_id2}.xml"; 50 | await this.SendFile(_gameServerOptions.XmlResourcePath, newsListFile); 51 | } 52 | 53 | /// 54 | /// For GT5 55 | /// 56 | /// 57 | /// 58 | /// 59 | [HttpGet] 60 | [Route("{serverId}/{region}/root.xml")] 61 | public async Task GetCategoryRoot(string serverId, string region) 62 | { 63 | string newsCategoryRootFile = $"news/{serverId}/{region}/root.xml"; 64 | 65 | // Note: The game will try 5 times, if missing 66 | await this.SendFile(_gameServerOptions.XmlResourcePath, newsCategoryRootFile); 67 | } 68 | 69 | /// 70 | /// For GT5 71 | /// 72 | /// 73 | /// 74 | /// 75 | [HttpGet] 76 | [Route("common/{newsId}/{imageId}.jpg")] 77 | public async Task GetNewsImage(int newsId, int imageId) 78 | { 79 | string newsFile = $"news/common/{newsId}/{imageId}.jpg"; 80 | await this.SendFile(_gameServerOptions.XmlResourcePath, newsFile); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/UserController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.Extensions.Logging; 4 | using Microsoft.AspNetCore.Authorization; 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | 11 | using System.Xml.Serialization; 12 | using System.IO; 13 | 14 | using GTGrimServer.Database.Controllers; 15 | using GTGrimServer.Database.Tables; 16 | using GTGrimServer.Filters; 17 | using GTGrimServer.Controllers; 18 | using GTGrimServer.Services; 19 | using GTGrimServer.Models; 20 | using GTGrimServer.Models.Xml; 21 | using GTGrimServer.Utils; 22 | 23 | namespace GTGrimServer.Helpers 24 | { 25 | /// 26 | /// Handles profile related requests. 27 | /// 28 | [ApiController] 29 | [Authorize] 30 | [PDIClient] 31 | [Route("[controller]")] 32 | [Produces("application/xml")] 33 | public class UserController : GrimControllerBase 34 | { 35 | private readonly ILogger _logger; 36 | private readonly UserDBManager _userDb; 37 | private readonly FriendDBManager _friendsDb; 38 | 39 | public UserController(PlayerManager players, 40 | ILogger logger, 41 | UserDBManager userDb, 42 | FriendDBManager friendsDb) 43 | : base(players) 44 | { 45 | _logger = logger; 46 | _userDb = userDb; 47 | _friendsDb = friendsDb; 48 | } 49 | 50 | /// 51 | /// General user fetching. 52 | /// 53 | /// 54 | /// 55 | [HttpGet] 56 | [Route("{userId}.xml")] 57 | public async Task Get(string userId) 58 | { 59 | var currentPlayer = Player; 60 | if (currentPlayer is null) 61 | { 62 | _logger.LogWarning("Unable to get current player for host '{host}'", Request.Host); 63 | return Unauthorized(); 64 | } 65 | 66 | UserProfile userProfile; 67 | 68 | // Is it self? 69 | if (currentPlayer?.Data?.PSNUserId?.Equals(userId) is true) 70 | { 71 | userProfile = UserProfile.FromDatabaseObject(currentPlayer.Data); 72 | } 73 | else 74 | { 75 | // Possible friend - Check if they're a friend before allowing to get their profile 76 | UserDTO userData = await _userDb.GetByPSNUserIdAsync(userId); 77 | if (!await _friendsDb.IsFriendedToUser(currentPlayer.Data.Id, userData.Id)) 78 | return Forbid(); 79 | 80 | userProfile = UserProfile.FromDatabaseObject(userData); 81 | } 82 | 83 | userProfile.BandUp = 1024; 84 | userProfile.BandDown = 1024; 85 | userProfile.BandUpdateTime = DateTime.Now.ToRfc3339String(); 86 | userProfile.BandTest = 1024; 87 | return Ok(userProfile); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/GrimRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using System.IO; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot(ElementName = "grim")] 12 | public class GrimRequest 13 | { 14 | [XmlElement("command")] 15 | public string Command { get; set; } 16 | [XmlElement("params")] 17 | public GrimRequestParams Params { get; set; } 18 | 19 | 20 | public GrimRequest() { } 21 | 22 | public static async Task Deserialize(Stream inputStream) 23 | { 24 | var serializer = new XmlSerializer(typeof(GrimRequest)); 25 | 26 | Stream ms = new MemoryStream(); 27 | await inputStream.CopyToAsync(ms); 28 | ms.Position = 0; 29 | 30 | GrimRequest requestReq = serializer.Deserialize(ms) as GrimRequest; 31 | return requestReq; 32 | } 33 | 34 | public static GrimRequest Deserialize(string inputXml) 35 | { 36 | var grimSerializer = new XmlSerializer(typeof(GrimRequest)); 37 | using var reader = new StringReader(inputXml); 38 | 39 | GrimRequest requestReq = grimSerializer.Deserialize(reader) as GrimRequest; 40 | return requestReq; 41 | } 42 | 43 | public bool TryGetParameterByIndex(int index, out GrimRequestParam param) 44 | { 45 | if (index < 0 || index >= Params.ParamList.Count) 46 | { 47 | param = null; 48 | return false; 49 | } 50 | 51 | param = Params.ParamList[index]; 52 | return true; 53 | } 54 | 55 | public bool TryGetParameterByKey(string key, out GrimRequestParam param) 56 | { 57 | param = Params.ParamList.FirstOrDefault(p => p.Key == key); 58 | if (param is not null && param.Text is null) 59 | param.Text = string.Empty; 60 | 61 | return param is not null; 62 | } 63 | 64 | public bool TryGetParameterIntByKey(string key, out int value) 65 | { 66 | value = 0; 67 | if (TryGetParameterByKey(key, out var param) && int.TryParse(param.Text, out value)) 68 | return true; 69 | 70 | return false; 71 | } 72 | 73 | public bool TryGetParameterLongByKey(string key, out long value) 74 | { 75 | value = 0; 76 | if (TryGetParameterByKey(key, out var param) && long.TryParse(param.Text, out value)) 77 | return true; 78 | 79 | return false; 80 | } 81 | } 82 | 83 | [XmlRoot("params")] 84 | public class GrimRequestParams 85 | { 86 | [XmlElement("param")] 87 | public List ParamList { get; set; } 88 | } 89 | 90 | [XmlRoot("param")] 91 | public class GrimRequestParam 92 | { 93 | [XmlAttribute("key")] 94 | public string Key { get; set; } 95 | [XmlText] 96 | public string Text { get; set; } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/UserSpecialDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Data; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class UserSpecialDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public UserSpecialDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM user_specials WHERE id = @Id", new { Id = id }); 31 | 32 | /// 33 | /// Gets all the specials of an user. 34 | /// 35 | /// Database Id of the user. 36 | /// Type of the special, GT6 always uses 3. -1 will retrieve all specials. 37 | /// Special object list. 38 | public async Task> GetAllPresentsOfUserAsync(long dbUserId, int type = -1) 39 | { 40 | if (type == -1) 41 | return await _con.QueryAsync(@"SELECT * FROM user_specials WHERE userid=@UserId AND type=@Type", new { UserId = dbUserId, Type = type }); 42 | else 43 | return await _con.QueryAsync(@"SELECT * FROM user_specials WHERE userid=@UserId", new { UserId = dbUserId }); 44 | } 45 | 46 | public async Task UpdateAsync(UserSpecialDTO uSpecialData) 47 | => await _con.ExecuteAsync(@"UPDATE user_specials WHERE id=@Id", uSpecialData); 48 | 49 | public async Task AddAsync(UserSpecialDTO uSpecialData) 50 | { 51 | var query = 52 | @"INSERT INTO user_specials (userid, type, key, value) 53 | VALUES(@UserId, @Type, @Key, @Value) 54 | returning id"; 55 | 56 | return await _con.ExecuteScalarAsync(query, new { uSpecialData.UserId, uSpecialData.Type, uSpecialData.Key, uSpecialData.Value }); 57 | } 58 | 59 | public async Task RemoveAsync(long id) 60 | => await _con.ExecuteAsync(@"DELETE FROM user_specials WHERE id=@id", new { Id = id }); 61 | 62 | public void CreateTable() 63 | { 64 | string query = 65 | @"CREATE TABLE IF NOT EXISTS user_specials ( 66 | id SERIAL PRIMARY KEY, 67 | userid BIGINT REFERENCES users(id), 68 | type INT, 69 | key TEXT, 70 | value TEXT 71 | );"; 72 | _con.Execute(query); 73 | 74 | string query2 = @"CREATE INDEX IF NOT EXISTS user_specials_userid_idx ON user_specials (userid)"; 75 | _con.Execute(query2); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/ActionLogList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | /// 12 | /// Represents a GT5 board's comment list. 13 | /// 14 | [XmlRoot("actionlog_list")] 15 | public class ActionLogList 16 | { 17 | [XmlElement("actionlog")] 18 | public List Logs { get; set; } = new(); 19 | } 20 | 21 | [XmlRoot("actionlog")] 22 | public class ActionLog 23 | { 24 | public static Dictionary Tags = new() 25 | { 26 | { "NG", "NEW_GAME" }, 27 | { "CB", "CAR_BUY" }, 28 | { "CR", "CAR_RIDE" }, 29 | { "FR", "FRIENDCAR_RIDE" }, 30 | { "LL", "LICENSE_LEVEL" }, 31 | { "LE", "LICENSE_EVENT" }, 32 | { "AL", "ASPEC_LEVEL" }, 33 | { "AE", "ASPEC_EVENT" }, 34 | { "BL", "BSPEC_LEVEL" }, 35 | { "BE", "BSPEC_EVENT" }, 36 | { "SE", "SPECIAL_EVENT" }, 37 | { "DC", "DRIVER_CLASS" }, 38 | { "GS", "GIFT_SPECIAL" }, 39 | { "GM", "GIFT_MUSEUMCARD" }, 40 | { "GT", "GIFT_TUNEPARTS" }, 41 | { "GO", "GIFT_OTHERPARTS" }, 42 | { "GD", "GIFT_DRIVER_ITEM" }, 43 | { "TU", "TROPHY_UNLOCK" }, 44 | }; 45 | 46 | /// 47 | /// Action Log Id. 48 | /// 49 | //[XmlAttribute("actionlog_id")] 50 | //public long Id { get; set; } 51 | 52 | /// 53 | /// Creation date of this comment. 54 | /// 55 | [XmlAttribute("create_time")] 56 | public string CreateTimeString { get; set; } 57 | [XmlIgnore] 58 | public DateTime CreateTime 59 | { 60 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 61 | set => CreateTimeString = value.ToRfc3339String(); 62 | } 63 | 64 | /// 65 | /// Type of action. 66 | /// 67 | //[XmlAttribute("action_type")] 68 | //public byte ActionType { get; set; } 69 | 70 | /// 71 | /// Author User Id of this comment. 72 | /// 73 | [XmlAttribute("value1")] 74 | public string Value1 { get; set; } 75 | 76 | /// 77 | /// Author User Id of this comment. 78 | /// 79 | [XmlAttribute("value2")] 80 | public string Value2 { get; set; } 81 | 82 | /// 83 | /// Author User Id of this comment. 84 | /// 85 | [XmlAttribute("value3")] 86 | public string Value3 { get; set; } 87 | 88 | /// 89 | /// Author User Id of this comment. 90 | /// 91 | [XmlAttribute("value4")] 92 | public string Value4 { get; set; } 93 | 94 | /// 95 | /// Author User Id of this comment. 96 | /// 97 | [XmlAttribute("value5")] 98 | public string Value5 { get; set; } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/CourseList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml.Serialization; 7 | using GTGrimServer.Utils; 8 | 9 | namespace GTGrimServer.Models.Xml 10 | { 11 | [XmlRoot(ElementName = "course_list")] 12 | public class CourseList 13 | { 14 | [XmlElement("course")] 15 | public List Courses { get; set; } 16 | } 17 | 18 | /// 19 | /// GT6 Courses 20 | /// 21 | [XmlRoot("course")] 22 | public class Course 23 | { 24 | [XmlAttribute("course_id")] 25 | public long CourseId { get; set; } 26 | 27 | [XmlAttribute("create_time")] 28 | public string CreateTimeString { get; set; } 29 | [XmlIgnore] 30 | public DateTime CreateTime 31 | { 32 | get => DateTimeExtensions.FromRfc3339String(CreateTimeString); 33 | set => CreateTimeString = value.ToRfc3339String(); 34 | } 35 | 36 | [XmlAttribute("update_time")] 37 | public string UpdateTimeString { get; set; } 38 | [XmlIgnore] 39 | public DateTime UpdateTime 40 | { 41 | get => DateTimeExtensions.FromRfc3339String(UpdateTimeString); 42 | set => UpdateTimeString = value.ToRfc3339String(); 43 | } 44 | 45 | /// 46 | /// PSN Name of the user that owns this course (but not the creator). 47 | /// 48 | [XmlAttribute(AttributeName = "user_id")] 49 | public string OwnerId { get; set; } 50 | 51 | /// 52 | /// 0 = Awaiting Test Drive, 1 = Complete, 2 = Public 53 | /// 54 | [XmlAttribute("status")] 55 | public int Status { get; set; } 56 | 57 | [XmlAttribute("photo_id")] 58 | public long PhotoId { get; set; } 59 | 60 | [XmlAttribute("title")] 61 | public string Title { get; set; } 62 | 63 | [XmlAttribute("comment")] 64 | public string Comment { get; set; } 65 | 66 | [XmlAttribute("title_hidden")] 67 | public int TitleHidden { get; set; } 68 | 69 | [XmlAttribute("comment_hidden")] 70 | public int CommentHidden { get; set; } 71 | 72 | [XmlAttribute("photo_hidden")] 73 | public int PhotoHidden { get; set; } 74 | 75 | /// 76 | /// Theme name. Important: For lobbies, this has to be the scenery course code. 77 | /// 78 | [XmlAttribute("theme")] 79 | public string Theme { get; set; } 80 | 81 | [XmlAttribute("length")] 82 | public int Length { get; set; } 83 | 84 | [XmlAttribute("one_way")] 85 | public int OneWay { get; set; } 86 | 87 | [XmlAttribute("corners")] 88 | public int Corners { get; set; } 89 | 90 | /// 91 | /// Name of the creator that created this course. 92 | /// 93 | [XmlAttribute("source_user_id")] 94 | public string OriginalCreator { get; set; } 95 | 96 | [XmlAttribute("straight")] 97 | public int Straight { get; set; } 98 | 99 | [XmlAttribute("height")] 100 | public int Height { get; set; } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /REGARDING_OTHER_ONLINE_SERVERS.md: -------------------------------------------------------------------------------- 1 | # Regarding "Other" Online Servers 2 | 3 | This repository is the first and only implementation of a custom server for either GT5, or GT6 back in 2021. If you're here from the GT5 MM/GT6 Spec II mod, note that this was only written to spread awareness for people potentially wanting to use online servers (aside from the LAN capabilities), neither mods block ability to use online servers. This is a statement I wish I did not have to make, but as it stands my mods (and especially Master Mod) stands in the middle of something I did not want to happen for the sake of the community. 4 | 5 | I've been aware that other projects have been using this very source project **out of my will**, such as projects run by a certain 'Irekej'. 6 | 7 | I initially released a proof of concept (in the event that I am unable to release anything, just incase), while working on an actual server. 8 | 9 | This proof-of-concept, is, well, a PoC, and absolutely not suitable for wide usage. Why? There is no security measures whatsoever: 10 | 11 | * **IPs can be grabbed** 12 | * MITM (man-in-the-middle) possible as there is no HTTPS/SSL encryption, forgery is possible so is DoS (denial of service) 13 | * No NP ticket authentication whatsoever, **you can pretend to be other users** 14 | * **No security on the database itself** (likely breaches a few laws out there) 15 | 16 | Most importantly **it still requires PSN and a jailbroken console**, which is a big no-no. It's essentially hijacking sony's servers and playing with other users on hacked hardware, meaning your account can be banned at any given time, whether by detection, or by other user reports. 17 | 18 | I tried to contact the owner of this project for a compromise (for instance, using RPCS3's RPCN, or using the game's built-in no-PSN features), but I was faced with constant childish remarks, **little credit**, and they simply pretended to agree in taking proper actions to host such a project. 19 | 20 | --- 21 | 22 | This person in particular was well aware that I was working on my own and I have kindly asked not to host theirs using this proof-of-concept to avoid any potential problems with Sony. They could in fact, revoke the NP commnunication key for either games at any given time, which would severly hamper any reverse-engineering efforts (they have no reverse-engineering knowledge). Unfortunately they still proceeded and I've seen them first hand host servers with their friends, and then it grew out of proportion into an actual 'project'. 23 | 24 | This whole deal has been fueled by what seems to be a lack of patience from the south-american Gran Turismo community which was blindly used to ensure some level of presence and advertisement. To this day I am still seeing this project advertised without any credit whatsoever. Any broken or unimplemented feature that you might see on there are solely because no one knows how to get them working. 25 | 26 | The actual server I was working on was in fact, for the most part, complete (yes, including [Gran Turismo TV](https://www.youtube.com/watch?v=CR6LR0b2_ZE&t=79s), Online BSpec, all community features, GT6 Quickmatching, Clubs, and more). I've put it aside due to a lack of motivation from this incident. 27 | 28 | **Until a project can be created in a responsible manner, this repository (and my other, actual server), will remain this way.** If you're experienced and well minded to resume this project, feel free to reach out. 29 | 30 | [Some conversations here.](https://imgur.com/a/xl9qgpK) 31 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/event/dev/event_list_gt5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <JP>The home of the finest events in Gran Turismo</JP> 8 | <US>The home of the finest events in Gran Turismo</US> 9 | <GB>The home of the finest events in Gran Turismo</GB> 10 | <FR>The home of the finest events in Gran Turismo</FR> 11 | <DE>The home of the finest events in Gran Turismo</DE> 12 | <IT>The home of the finest events in Gran Turismo</IT> 13 | <ES>The home of the finest events in Gran Turismo</ES> 14 | <PT>The home of the finest events in Gran Turismo</PT> 15 | <NL>The home of the finest events in Gran Turismo</NL> 16 | <RU>The home of the finest events in Gran Turismo</RU> 17 | <KR>The home of the finest events in Gran Turismo</KR> 18 | <TW>The home of the finest events in Gran Turismo</TW> 19 | <EL>The home of the finest events in Gran Turismo</EL> 20 | <TR>The home of the finest events in Gran Turismo</TR> 21 | <PL>The home of the finest events in Gran Turismo</PL> 22 | <CZ>The home of the finest events in Gran Turismo</CZ> 23 | <HU>The home of the finest events in Gran Turismo</HU> 24 | <BP>The home of the finest events in Gran Turismo</BP> 25 | <MS>The home of the finest events in Gran Turismo</MS> 26 | 27 | 28 | Test Descriptions 29 | Test Descriptions 30 | Test Descriptions 31 | Test Descriptions 32 | Test Descriptions 33 | Test Descriptions 34 | Test Descriptions 35 | Test Descriptions 36 | Test Descriptions 37 | Test Descriptions 38 | Test Descriptions 39 | Test Descriptions 40 | Test Descriptions 41 | Test Descriptions 42 | Test Descriptions 43 | Test Descriptions 44 | Test Descriptions 45 | Test Descriptions 46 | Test Descriptions 47 | 48 | 49 | 50 | 51 | <JP>Test</JP> 52 | <US>Test</US> 53 | <GB>Test</GB> 54 | <FR>Test</FR> 55 | <DE>Test</DE> 56 | <IT>Test</IT> 57 | <ES>Test</ES> 58 | <PT>Test</PT> 59 | <NL>Test</NL> 60 | <RU>Test</RU> 61 | <KR>Test</KR> 62 | <TW>Test</TW> 63 | <EL>Test</EL> 64 | <TR>Test</TR> 65 | <PL>Test</PL> 66 | <CZ>Test</CZ> 67 | <HU>Test</HU> 68 | <BP>Test</BP> 69 | <MS>Test</MS> 70 | 71 | 1002268 72 | 73 | 74 | 146 75 | 76 | 77 | 0 78 | rick.png 79 | 80 | 5 81 | 0 82 | 0 83 | 0 84 | 85 | 0 86 | 2021-04-01T16:39:04.5176312+02:00 87 | 2022-04-01T16:39:04.5176312+02:00 88 | 2021-04-01T16:39:04.5176312+02:00 89 | 1002268 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/serverlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gt 5 | 23 | 24 | game 25 | 39 | 40 | network 41 | 50 | 51 | grim_http 52 | 53 | 61 | 62 | grim2_http 63 | 70 | 71 | gtacademy 72 | 74 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/dev/serverlist.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gt 5 | 21 | 22 | game 23 | 37 | 38 | network 39 | 48 | 49 | grim_http 50 | 51 | 52 | 192.168.0.24 53 | 80 54 | 192.168.0.24 55 | http://192.168.0.24 56 | 57 | 65 | 66 | grim2_http 67 | 74 | 75 | gtacademy 76 | 78 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Data/TVController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Threading.Tasks; 10 | using System.IO; 11 | 12 | using GTGrimServer.Filters; 13 | using GTGrimServer.Config; 14 | using GTGrimServer.Utils; 15 | 16 | namespace GTGrimServer.Controllers 17 | { 18 | [ApiController] 19 | [PDIClient] 20 | [Authorize] 21 | [Route("/")] // Images grab the /data/ endpoint rather than /data2/ 22 | [Produces("application/xml")] 23 | public class TVController : ControllerBase 24 | { 25 | private readonly ILogger _logger; 26 | private readonly GameServerOptions _gameServerOptions; 27 | 28 | public TVController(IOptions options, ILogger logger) 29 | { 30 | _logger = logger; 31 | _gameServerOptions = options.Value; 32 | } 33 | 34 | [HttpGet] 35 | [Route("/data2/[controller]/{server}/{region}/tv2_{fileRegion}_{arg}.xml")] 36 | public async Task GetTV(string server, string region, string fileRegion, string arg) 37 | { 38 | if (arg.Equals("root")) 39 | await GetCategoryRoot(server, region, fileRegion); 40 | else if (arg.StartsWith("l_") && arg.Length > 2 && int.TryParse(arg.AsSpan(2), out int listId)) 41 | { 42 | await GetTVList(server, region, fileRegion, listId); 43 | } 44 | else if (arg.StartsWith("s_") && arg.Length > 2 && int.TryParse(arg.AsSpan(2), out int setId)) 45 | { 46 | await GetTVSet(server, region, fileRegion, setId); 47 | } 48 | else if (arg.Length > 0 && int.TryParse(arg, out int itemId)) 49 | await GetTVItem(server, region, fileRegion, itemId); 50 | else 51 | { 52 | // Handle issue 53 | } 54 | } 55 | 56 | [HttpGet] 57 | [Route("/data/[controller]/common/tv2_l_{type}_{fileId:int}.img")] 58 | public async Task GetTVListImage(string type, int fileId) 59 | { 60 | string tvListImageFile = $"data/tv/common/tv2_l_{type}_{fileId}.img"; 61 | await this.SendFile(_gameServerOptions.XmlResourcePath, tvListImageFile); 62 | } 63 | 64 | public async Task GetCategoryRoot(string server, string region, string region2) 65 | { 66 | string tvRootFile = $"data2/tv/{server}/{region}/tv2_{region2}_root.xml"; 67 | await this.SendFile(_gameServerOptions.XmlResourcePath, tvRootFile); 68 | } 69 | 70 | public async Task GetTVList(string server, string region, string fileRegion, int listId) 71 | { 72 | string tvListFile = $"data2/tv/{server}/{region}/tv2_{fileRegion}_l_{listId}.xml"; 73 | await this.SendFile(_gameServerOptions.XmlResourcePath, tvListFile); 74 | } 75 | 76 | public async Task GetTVSet(string server, string region, string fileRegion, int setId) 77 | { 78 | string tvSetFile = $"data2/tv/{server}/{region}/tv2_{fileRegion}_s_{setId}.xml"; 79 | await this.SendFile(_gameServerOptions.XmlResourcePath, tvSetFile); 80 | } 81 | 82 | public async Task GetTVItem(string server, string region, string fileRegion, int itemId) 83 | { 84 | string tvItemFile = $"data2/tv/{server}/{region}/tv2_{fileRegion}_{itemId}.xml"; 85 | await this.SendFile(_gameServerOptions.XmlResourcePath, tvItemFile); 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /GTGrimServer/Sony/NPTicket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Buffers.Binary; 6 | using System.IO; 7 | 8 | using Syroot.BinaryData; 9 | using System.Text; 10 | namespace GTGrimServer.Sony 11 | { 12 | public class NPTicket 13 | { 14 | public int VersionMajor { get; set; } 15 | public int VersionMinor { get; set; } 16 | 17 | public byte[] Signature { get; set; } 18 | public ulong IssuedDate { get; set; } 19 | public ulong ExpiryDate { get; set; } 20 | public ulong UserId { get; set; } 21 | public string OnlineId { get; set; } 22 | public string Region { get; set; } 23 | public NPLanguage Language { get; set; } 24 | 25 | public string Domain { get; set; } 26 | public byte[] ServiceID { get; set; } 27 | 28 | public static NPTicket FromBuffer(byte[] buffer) 29 | { 30 | using var ms = new MemoryStream(buffer); 31 | return FromStream(ms); 32 | } 33 | 34 | public static NPTicket FromStream(Stream stream) 35 | { 36 | var ticket = new NPTicket(); 37 | var bs = new BinaryStream(stream); 38 | bs.ByteConverter = ByteConverter.Big; 39 | 40 | ticket.VersionMajor = bs.ReadByte() >> 4; 41 | ticket.VersionMinor = bs.ReadByte(); 42 | 43 | if (ticket.VersionMajor >= 4) 44 | bs.Position = 0x10; 45 | else 46 | bs.Position = 0x0C; 47 | 48 | ticket.Signature = (byte[])ReadNext(bs); 49 | ReadNext(bs); 50 | ticket.IssuedDate = (ulong)ReadNext(bs); 51 | ticket.ExpiryDate = (ulong)ReadNext(bs); 52 | ticket.UserId = (ulong)ReadNext(bs); 53 | ticket.OnlineId = (string)ReadNext(bs); 54 | 55 | byte[] localeData = (byte[])ReadNext(bs); 56 | ticket.Region = Encoding.UTF8.GetString(localeData.AsSpan(0, 2)); 57 | ticket.Language = (NPLanguage)BinaryPrimitives.ReadInt16BigEndian(localeData.AsSpan()[2..]); 58 | 59 | ticket.Domain = (string)ReadNext(bs); 60 | ticket.ServiceID = (byte[])ReadNext(bs); 61 | 62 | return ticket; 63 | } 64 | 65 | private static object ReadNext(BinaryStream bs) 66 | { 67 | TicketFieldType type = (TicketFieldType)bs.ReadInt16(); 68 | short dataLen = bs.ReadInt16(); 69 | 70 | switch (type) 71 | { 72 | case TicketFieldType.Empty: 73 | return null; 74 | case TicketFieldType.UInt32: 75 | return bs.ReadUInt32(); 76 | case TicketFieldType.UInt64: 77 | return bs.ReadUInt64(); 78 | case TicketFieldType.String: 79 | return bs.ReadString(dataLen).TrimEnd('\0'); 80 | case TicketFieldType.Timestamp: 81 | return bs.ReadUInt64(); 82 | case TicketFieldType.Binary: 83 | return bs.ReadBytes(dataLen); 84 | default: 85 | return null; 86 | } 87 | } 88 | } 89 | 90 | public enum TicketFieldType 91 | { 92 | Empty = 0, 93 | UInt32 = 1, 94 | UInt64 = 2, 95 | String = 4, 96 | Timestamp = 7, 97 | Binary = 8, 98 | } 99 | 100 | public enum NPLanguage 101 | { 102 | Japanese, 103 | English, 104 | French, 105 | Spanish, 106 | German, 107 | Italian, 108 | Norwegian, 109 | Portugese, 110 | Russian, 111 | Korean, 112 | Chinese 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /GTGrimServer/Resources/serverlist_gt5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gt5 5 | 20 | 21 | gt5p 22 | 31 | 32 | game 33 | 43 | 44 | grim_http 45 | 46 | 55 | 56 | grim2_http 57 | 64 | 65 | gtacademy 66 | 69 | 70 | pace 71 | 77 | -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/CourseDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Data; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class CourseDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public CourseDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM courses WHERE id = @Id", new { Id = id }); 31 | 32 | /// 33 | /// Gets all the courses of an user. 34 | /// 35 | /// Database Id of the user. 36 | /// Course object list. 37 | public async Task> GetAllCoursesOfUser(long id) 38 | => await _con.QueryAsync(@"SELECT * FROM courses WHERE id=@Id", new { Id = id }); 39 | 40 | public async Task UpdateAsync(CourseDTO pData) 41 | => await _con.ExecuteAsync(@"UPDATE courses WHERE id=@Id AND friendid=@FriendId", pData); 42 | 43 | public async Task AddAsync(CourseDTO friendData) 44 | { 45 | var query = 46 | @"INSERT INTO courses (user_id, create_time, update_time, status, photo_id, 47 | title, comment, title_hidden, comment_hidden, photo_hidden, theme, length, one_way, source_user_id) 48 | VALUES 49 | (@UserId, @CreateTime, @UpdateTime, @Status, @PhotoId, @Title, @Comment, @TitleHidden, 50 | @CommentHidden, @PhotoHidden, @Theme, @Length, @OneWay, @SourceUserId, @Straight, @Height) 51 | returning id"; 52 | 53 | return await _con.ExecuteScalarAsync(query, new { friendData.UserId, friendData.FriendId }); 54 | } 55 | 56 | public async Task RemoveAsync(long id) 57 | => await _con.ExecuteAsync(@"DELETE FROM courses WHERE id=@id", new { Id = id }); 58 | 59 | /// 60 | /// Removes a course of an user. 61 | /// 62 | /// Database Id of the user. 63 | /// Database Id of the course. 64 | /// 65 | public async Task RemoveCourseAsync(long userId, long courseId) 66 | => await _con.ExecuteAsync(@"DELETE FROM courses WHERE userid=@UserId AND id=@Id", new { UserId = userId, Id = courseId }); 67 | 68 | public void CreateTable() 69 | { 70 | string query = 71 | @"CREATE TABLE IF NOT EXISTS courses ( 72 | id SERIAL PRIMARY KEY, 73 | user_id BIGINT REFERENCES users(id), 74 | create_time TIMESTAMP WITHOUT TIME ZONE, 75 | update_time TIMESTAMP WITHOUT TIME ZONE, 76 | status INTEGER, 77 | photo_id BIGINT, 78 | title TEXT, 79 | comment TEXT, 80 | title_hidden INTEGER, 81 | comment_hidden INTEGER, 82 | photo_hidden INTEGER, 83 | theme TEXT, 84 | one_way INTEGER, 85 | source_user_id BIGINT REFERENCES users(id), 86 | straight INTEGER, 87 | height INTEGER 88 | );"; 89 | 90 | _con.Execute(query); 91 | 92 | string query2 = @"CREATE INDEX IF NOT EXISTS courses_user_id_idx ON courses (user_id)"; 93 | _con.Execute(query2); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/RankingController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Filters; 16 | using GTGrimServer.Config; 17 | using GTGrimServer.Utils; 18 | using GTGrimServer.Models; 19 | using GTGrimServer.Models.Xml; 20 | 21 | namespace GTGrimServer.Controllers 22 | { 23 | /// 24 | /// Event Rankings 25 | /// 26 | [ApiController] 27 | [PDIClient] 28 | [Authorize] 29 | [Route("/ap/[controller]")] 30 | [Produces("application/xml")] 31 | public class RankingController : ControllerBase 32 | { 33 | private readonly ILogger _logger; 34 | private readonly GameServerOptions _gsOptions; 35 | 36 | public RankingController(IOptions options, ILogger logger) 37 | { 38 | _logger = logger; 39 | _gsOptions = options.Value; 40 | } 41 | 42 | [HttpGet] 43 | public async Task Get() 44 | { 45 | GrimRequest requestReq = await GrimRequest.Deserialize(Request.Body); 46 | if (requestReq is null) 47 | { 48 | // Handle 49 | var badReq = GrimResult.FromInt(-1); 50 | return BadRequest(badReq); 51 | } 52 | 53 | _logger.LogDebug("<- Got ranking request: {command}", requestReq.Command); 54 | 55 | switch (requestReq.Command) 56 | { 57 | case "ranking.calc2": 58 | return OnGetCalc2Ranking(requestReq); 59 | case "ranking.getCount": 60 | return OnGetCount(requestReq); 61 | case "ranking.getListFriends": 62 | return OnGetFriendList(requestReq); 63 | } 64 | 65 | _logger.LogDebug("<- Got unknown ranking command: {command}", requestReq.Command); 66 | var badReqs = GrimResult.FromInt(-1); 67 | return BadRequest(badReqs); 68 | } 69 | 70 | public ActionResult OnGetCount(GrimRequest request) 71 | { 72 | if (!request.TryGetParameterByKey("board_id", out var boardIdParam)) 73 | { 74 | _logger.LogWarning("Got ranking getCount request without 'board_id'"); 75 | return BadRequest(); 76 | } 77 | 78 | return Ok(GrimResult.FromInt(1)); 79 | } 80 | 81 | public ActionResult OnGetFriendList(GrimRequest request) 82 | { 83 | if (!request.TryGetParameterByKey("board_id", out var boardIdParam)) 84 | { 85 | _logger.LogWarning("Got ranking getCount request without 'board_id'"); 86 | return BadRequest(); 87 | } 88 | 89 | var rankingList = new RankingList(); 90 | return Ok(rankingList); 91 | } 92 | 93 | public ActionResult OnGetCalc2Ranking(GrimRequest request) 94 | { 95 | if (!request.TryGetParameterByKey("board_id", out var boardIdParam)) 96 | { 97 | _logger.LogWarning("Got ranking calc2 request without 'board_id'"); 98 | return BadRequest(); 99 | } 100 | 101 | if (!request.TryGetParameterByKey("user_id", out var userIdParam)) 102 | { 103 | _logger.LogWarning("Got ranking calc2 request without 'user_id'"); 104 | return BadRequest(); 105 | } 106 | 107 | var rankingList = new RankingList(); 108 | return Ok(rankingList); 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/PhotoDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Data; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class PhotoDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public PhotoDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM photos WHERE id = @Id", new { Id = id }); 31 | 32 | /// 33 | /// Gets the database id of the user that created an image by id. 34 | /// 35 | /// 36 | /// 37 | public async Task GetAuthorIdOfPhotoAsync(long id) 38 | => await _con.QueryFirstOrDefaultAsync(@"SELECT user_id FROM photos WHERE id = @Id", new { Id = id }); 39 | 40 | /// 41 | /// Gets the count of photos that an user has. 42 | /// 43 | /// Database id of the user's id. 44 | /// 45 | public async Task GetPhotoCountOfUserAsync(long id) 46 | => await _con.QueryFirstOrDefaultAsync(@"SELECT COUNT(id) FROM photos WHERE user_id = @UserId", new { UserId = id }); 47 | 48 | /// 49 | /// Gets all the photos of an user. 50 | /// 51 | /// Database Id of the user. 52 | /// Course object list. 53 | public async Task> GetAllPhotosOfUser(long userId) 54 | => await _con.QueryAsync(@"SELECT * FROM photos WHERE user_id=@UserId", new { UserId = userId }); 55 | 56 | public async Task UpdateAsync(PhotoDTO pData) 57 | => await _con.ExecuteAsync(@"UPDATE photos WHERE id=@Id", pData); 58 | 59 | public async Task AddAsync(PhotoDTO pData) 60 | { 61 | var query = 62 | @"INSERT INTO photos (user_id, create_time, comment, car_name, place) 63 | VALUES 64 | (@UserId, @CreateTime, @Comment, @CarName, @Place) 65 | returning id"; 66 | 67 | return await _con.ExecuteScalarAsync(query, new { pData.UserId, pData.CreateTime, pData.Comment, pData.CarName, pData.Place }); 68 | } 69 | 70 | public async Task RemoveAsync(long id) 71 | => await _con.ExecuteAsync(@"DELETE FROM photos WHERE id=@Id", new { Id = id }); 72 | 73 | /// 74 | /// Removes a photo of an user. 75 | /// 76 | /// Database Id of the user. 77 | /// Database Id of the photo. 78 | /// 79 | public async Task RemovePhotoAsync(long userId, long photoId) 80 | => await _con.ExecuteAsync(@"DELETE FROM photos WHERE user_id=@UserId AND id=@Id", new { UserId = userId, Id = photoId }); 81 | 82 | public void CreateTable() 83 | { 84 | string query = 85 | @"CREATE TABLE IF NOT EXISTS photos ( 86 | id BIGSERIAL PRIMARY KEY, 87 | user_id BIGINT REFERENCES users(id), 88 | create_time TIMESTAMP WITHOUT TIME ZONE, 89 | comment TEXT, 90 | car_name TEXT, 91 | place TEXT 92 | );"; 93 | 94 | _con.Execute(query); 95 | 96 | string query2 = @"CREATE INDEX IF NOT EXISTS photos_user_id_idx ON photos (user_id)"; 97 | _con.Execute(query2); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /GTGrimServer/GTConstants.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer 7 | { 8 | public class GTConstants 9 | { 10 | public const string PDIUserAgent = "pdistd-http library"; 11 | public const string GT6ServiceID_EU = "EP9001-BCES01893_00"; 12 | 13 | public const int MaxPhotoWidth = 3840; 14 | public const int MaxPhotoHeight = 2160; 15 | public const int MaxPhotoSize = 6_000_000; 16 | public const int MaxPhotos = 8; 17 | } 18 | 19 | public enum PFSType 20 | { 21 | // Times in seconds - Add 978307200 (from 2001-01-01) 22 | 23 | GT5P_EU_SPEC1_2 = 226279597, // GT5P-UK-TOTTORI-562314254 24 | GT5P_US_SPEC3 = 227901480, // GT5P-US-SONORA-550937027 25 | GT5P_EU_SPEC3_DIGITAL = 248285681, // GT5P-UK-TOTTORI-562314254 26 | 27 | GT5_TT_EU = 282245958, // ACADEMY-UK-PISCINAS-77828733 28 | 29 | GT5_KIOSK_DEMO_EU = 300811023, // GT5E-ARABIAN-312107402 30 | 31 | GT5_JP_V2_11 = 381768656, // GT5-JP-SAHARA-568201135 32 | GT5_UK_V2_11 = 381769565, // GT5-UK-KALAHARI-37863889 33 | GT5_US_V2_11 = 381769680, // GT5-US-PATAGONIAN-22798263 34 | GT5_TW_V2_11 = 381769750, // GT5-TW-TAKLAMAKAN-63706075 35 | 36 | GTA_2013 = 392922939, // ACADEMY-CHOLISTAN-901501638 37 | 38 | GT6_GAMESCOM_DEMO = 398721401, // Build: 2013/08/20 19:56:41 39 | 40 | GT6_V1_00 = 405868366, // GT6-PISCINAS-323419048 41 | GT6_V1_02 = 408913268, 42 | GT6_V1_03 = 410907001, 43 | GT6_V1_04 = 412168771, 44 | GT6_V1_05 = 415371169, 45 | GT6_V1_06 = 417662393, 46 | GT6_V1_07 = 421135810, 47 | GT6_V1_08 = 422376318, 48 | GT6_V1_09 = 424224343, 49 | GT6_V1_10 = 427224741, 50 | GT6_V1_11 = 428497826, 51 | GT6_V1_12 = 431469971, 52 | GT6_V1_13 = 433078491, 53 | GT6_V1_14 = 437482543, 54 | GT6_V1_15 = 439831308, 55 | GT6_V1_16 = 445620800, 56 | GT6_V1_17 = 447401670, 57 | GT6_V1_18 = 449758481, 58 | GT6_V1_19 = 453817670, 59 | GT6_V1_20 = 456067192, 60 | GT6_V1_21 = 464012109, 61 | GT6_V1_22 = 469128998, 62 | } 63 | 64 | public enum GT5TrophyType 65 | { 66 | PLATINUM, 67 | ALL_GOLD, 68 | A_SPEC_LEVEL25, 69 | B_SPEC_LEVEL25, 70 | ENDING, 71 | ENDING2, 72 | RACE_EVENT_ENDURANCE, 73 | RACE_EVENT_EXTREME, 74 | RACE_EVENT_EXPERT, 75 | RACE_EVENT_PROFESSIONAL, 76 | RACE_EVENT_AMATEUR, 77 | RACE_EVENT_BEGINNER, 78 | LICENSE_S_CLEAR, 79 | LICENSE_IA_CLEAR, 80 | LICENSE_IB_CLEAR, 81 | LICENSE_IC_CLEAR, 82 | LICENSE_A_CLEAR, 83 | LICENSE_B_CLEAR, 84 | SPECIAL_EVENT_VETTEL_CLEAR, 85 | SPECIAL_EVENT_LOEB_CLEAR, 86 | SPECIAL_EVENT_GRAND_TOUR_CLEAR, 87 | SPECIAL_EVENT_RALLY_CLEAR, 88 | SPECIAL_EVENT_AMG_CLEAR, 89 | SPECIAL_EVENT_STIG_CLEAR, 90 | SPECIAL_EVENT_GORDON_CLEAR, 91 | SPECIAL_EVENT_KART_CLEAR, 92 | FIRST_WIN, 93 | FIRST_WIN_B_SPEC, 94 | UPLOAD_PHOTO, 95 | UPLOAD_COURSE, 96 | TUNING_MANIAC, 97 | MODIFYING_CAR, 98 | DRIFT_BEGINNER, 99 | USE_DATA_LOGGER, 100 | CUSTOM_BGM, 101 | MILEAGE, 102 | COLLECTOR, 103 | MILLIONAIRE, 104 | OBTAIN_BEFORE50S_CAR, 105 | OBTAIN_MEMORIAL_CARS, 106 | OBTAIN_LOVED_CAR, 107 | OBTAIN_EXPENSIVE_CAR, 108 | OBTAIN_SUPER_EXPENSIVE_CAR, 109 | OVER_300KMH, 110 | OVER_400KMH, 111 | BY_A_NARROW_MARGIN, 112 | BATTLE_RIVAL4WD, 113 | BATTLE_1967, 114 | GT_R_LAPTIME, 115 | STABLE_LAPTIME, 116 | B_SPEC_TROPHY, 117 | B_SPEC_TROPHY_VETERAN_DRIVER, 118 | COLLECT_256_PAINT_COLOR, 119 | PENNILESS, 120 | DROP_PARTS, 121 | OVERTURNED, 122 | TAKE_A_PHOTO_OF_HONDA_AT_HONDA, 123 | MAN_HUNTER, 124 | WOMAN_PORTRAIT 125 | } 126 | 127 | public enum GameType 128 | { 129 | Unknown, 130 | GT5, 131 | GT6 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/GTMailController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Config; 16 | using GTGrimServer.Utils; 17 | using GTGrimServer.Models; 18 | using GTGrimServer.Filters; 19 | using GTGrimServer.Models.Xml; 20 | 21 | namespace GTGrimServer.Controllers 22 | { 23 | /// 24 | /// Provides news to the player. 25 | /// 26 | [ApiController] 27 | [PDIClient] 28 | [Authorize] 29 | [Route("/ap/[controller]")] 30 | [Produces("application/xml")] 31 | public class GTMailController : ControllerBase 32 | { 33 | private readonly ILogger _logger; 34 | private readonly GameServerOptions _gsOptions; 35 | 36 | public GTMailController(IOptions options, ILogger logger) 37 | { 38 | _logger = logger; 39 | _gsOptions = options.Value; 40 | } 41 | 42 | [HttpPost] 43 | public async Task Post() 44 | { 45 | GrimRequest requestReq = await GrimRequest.Deserialize(Request.Body); 46 | if (requestReq is null) 47 | return BadRequest(); 48 | 49 | switch (requestReq.Command) 50 | { 51 | case "mail.getlist": 52 | return OnGetMail(requestReq); 53 | case "mail.send": 54 | return OnSendMail(requestReq); 55 | } 56 | 57 | _logger.LogDebug($"Received unimplemented mail command: {requestReq.Command}"); 58 | 59 | return BadRequest(); 60 | } 61 | 62 | private ActionResult OnGetMail(GrimRequest request) 63 | { 64 | if (!request.TryGetParameterByKey("mail_id", out var mailId)) 65 | { 66 | _logger.LogWarning($"Got get mail request with missing 'mail_id' parameter"); 67 | return BadRequest(); 68 | } 69 | 70 | if (!request.TryGetParameterByKey("by", out var sortType)) 71 | { 72 | _logger.LogWarning($"Got get mail request with missing 'by' parameter"); 73 | return BadRequest(); 74 | } 75 | 76 | /* 77 | var result = new Mail() 78 | { 79 | FromUsername = "PSN_Name_Author", 80 | ToUsername = "-- PSN_Name_Destination", 81 | FromNickname = "-- from nickname --", 82 | ToNickname = "-- to nickname --", 83 | Body = "-- body --", 84 | MailId = 0, 85 | Subject = "-- subject --", 86 | CreateTime = DateTime.Now 87 | }; 88 | */ 89 | return Ok(); 90 | } 91 | 92 | private ActionResult OnSendMail(GrimRequest request) 93 | { 94 | if (!request.TryGetParameterByKey("to", out var toParam)) 95 | { 96 | _logger.LogWarning($"Got get mail send request with missing 'to' parameter"); 97 | return BadRequest(); 98 | } 99 | else if (!request.TryGetParameterByKey("subject", out var subjectParam)) 100 | { 101 | _logger.LogWarning($"Got get mail send request with missing 'subject' parameter"); 102 | return BadRequest(); 103 | } 104 | else if (!request.TryGetParameterByKey("body", out var bodyParam)) 105 | { 106 | _logger.LogWarning($"Got get mail send request with missing 'body' parameter"); 107 | return BadRequest(); 108 | } 109 | else if (!request.TryGetParameterByKey("mail_id", out var mailIdParam)) 110 | { 111 | _logger.LogWarning($"Got get mail send request with missing 'mail_id' parameter"); 112 | return BadRequest(); 113 | } 114 | 115 | // Mail list with 1 is sent 116 | var mailList = new MailList() 117 | { 118 | Mails = new List() 119 | { 120 | new Mail(), 121 | } 122 | }; 123 | 124 | return Ok(); 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/FriendDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Text; 6 | using System.Data; 7 | using System.Net; 8 | using System.Net.NetworkInformation; 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class FriendDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public FriendDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM friends WHERE id = @Id", new { Id = id }); 31 | 32 | /// 33 | /// Gets all the friends of an user. 34 | /// 35 | /// Database Id of the user. 36 | /// Friend object list. 37 | public async Task> GetAllFriendsOfUser(long id) 38 | => await _con.QueryAsync(@"SELECT * FROM friends WHERE userid=@Id", new { Id = id }); 39 | 40 | /// 41 | /// Returns whether an user is friended to another user. 42 | /// 43 | /// Database Id of the user. 44 | /// Friend object list. 45 | public async Task IsFriendedToUser(long userId, long targetId) 46 | { 47 | return await _con.ExecuteScalarAsync(@"SELECT 1 FROM friends WHERE userid = @UserId AND friendid = @FriendId", 48 | new { UserId = userId, TargetId = targetId }); 49 | } 50 | 51 | public async Task UpdateAsync(FriendDTO pData) 52 | => await _con.ExecuteAsync(@"UPDATE friends WHERE id=@Id AND friendid=@FriendId", pData); 53 | 54 | public async Task AddAsync(FriendDTO friendData) 55 | { 56 | var query = 57 | @"INSERT INTO friends (userid, friendid) 58 | VALUES(@UserId, @FriendId) 59 | returning id"; 60 | 61 | return await _con.ExecuteScalarAsync(query, new { friendData.UserId, friendData.FriendId }); 62 | } 63 | 64 | /// 65 | /// Updates an user's PSN friend list. 66 | /// 67 | /// Database Id of the user. 68 | /// List of user ids that are friends of the current user. 69 | /// 70 | public async Task UpdateFriendList(int userid, string[] currentFriendList) 71 | { 72 | var sb = new StringBuilder("DELETE FROM friends WHERE userid="); sb.Append(userid); sb.AppendLine(" AND friendid IN ("); 73 | sb.Append("SELECT id FROM users WHERE psn_user_id NOT IN ("); 74 | foreach (var friend in currentFriendList) 75 | { 76 | sb.Append('\''); sb.Append(friend.Replace("'", "''")); sb.Append('\''); 77 | } 78 | sb.Append(')'); sb.AppendLine(); 79 | sb.Append(')'); 80 | 81 | await _con.ExecuteAsync(sb.ToString()); 82 | 83 | // Add all friends that aren't in 84 | var addQuery = @$"INSERT INTO friends (userid, friendid) 85 | SELECT @UserId, id FROM users WHERE psn_user_id = ANY(@FriendListUserIds) AND id NOT IN ( 86 | SELECT friendid FROM friends WHERE userid=@UserId AND friendid != id 87 | )"; 88 | 89 | await _con.ExecuteAsync(addQuery, new { UserId = userid, FriendListUserIds = currentFriendList }); 90 | } 91 | 92 | public async Task RemoveAsync(long id) 93 | => await _con.ExecuteAsync(@"DELETE FROM friends WHERE id=@id", new { Id = id }); 94 | 95 | /// 96 | /// Removes a friend of an user. 97 | /// 98 | /// Database Id of the user. 99 | /// Database Id of the friend. 100 | /// 101 | public async Task RemoveFriendAsync(long userId, long friendId) 102 | => await _con.ExecuteAsync(@"DELETE FROM friends WHERE userid=@UserId AND friendid=@FriendId", new { UserId = userId, FriendId = friendId }); 103 | 104 | public void CreateTable() 105 | { 106 | string query = 107 | @"CREATE TABLE IF NOT EXISTS friends ( 108 | id SERIAL PRIMARY KEY, 109 | userid BIGINT REFERENCES users(id), 110 | friendid BIGINT REFERENCES users(id) 111 | );"; 112 | 113 | _con.Execute(query); 114 | 115 | string query2 = @"CREATE INDEX IF NOT EXISTS friends_userid_idx ON friends (userid)"; 116 | _con.Execute(query2); 117 | 118 | string query3 = @"CREATE INDEX IF NOT EXISTS friends_friendid_idx ON friends (friendid)"; 119 | _con.Execute(query3); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/CourseController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Config; 16 | using GTGrimServer.Utils; 17 | using GTGrimServer.Database.Controllers; 18 | using GTGrimServer.Models; 19 | using GTGrimServer.Models.Xml; 20 | using GTGrimServer.Filters; 21 | using GTGrimServer.Services; 22 | 23 | namespace GTGrimServer.Controllers 24 | { 25 | /// 26 | /// Provides news to the player. 27 | /// 28 | [ApiController] 29 | [PDIClient] 30 | [Authorize] 31 | [Produces("application/xml")] 32 | [Route("")] 33 | public class CourseController : GrimControllerBase 34 | { 35 | private readonly ILogger _logger; 36 | private readonly GameServerOptions _gsOptions; 37 | private readonly CourseDBManager _courses; 38 | private readonly UserDBManager _users; 39 | 40 | public CourseController(PlayerManager players, 41 | CourseDBManager courses, 42 | UserDBManager users, 43 | IOptions options, 44 | ILogger logger) 45 | : base(players) 46 | { 47 | _logger = logger; 48 | _gsOptions = options.Value; 49 | _users = users; 50 | _courses = courses; 51 | } 52 | 53 | [HttpPost] 54 | [Route("/ap/[controller]")] 55 | public async Task Post() 56 | { 57 | var player = Player; 58 | if (player is null) 59 | { 60 | _logger.LogWarning("Could not get current player for host {host}", Request.Host); 61 | return Unauthorized(); 62 | } 63 | 64 | if (_gsOptions.GameType != GameType.GT6) 65 | { 66 | _logger.LogWarning("Got course getlist request on non GT6"); 67 | return BadRequest(); 68 | } 69 | 70 | GrimRequest requestReq = await GrimRequest.Deserialize(Request.Body); 71 | if (requestReq is null) 72 | { 73 | // Handle 74 | var badReq = GrimResult.FromInt(-1); 75 | return BadRequest(badReq); 76 | } 77 | 78 | _logger.LogDebug("<- Got course request: {command}", requestReq.Command); 79 | 80 | switch (requestReq.Command) 81 | { 82 | case "course.getlist": 83 | return await OnGetList(requestReq, player); 84 | case "course.update": 85 | return await OnUpdateCourse(requestReq, player); 86 | } 87 | 88 | _logger.LogDebug("<- Got unknown course command: {command}", requestReq.Command); 89 | var badReqs = GrimResult.FromInt(-1); 90 | return BadRequest(badReqs); 91 | } 92 | 93 | [HttpGet] 94 | [Route("/[controller]/data/{courseId:long}.dat")] 95 | public async Task GetTrack(long courseId) 96 | { 97 | string tedFile = $"course/data/{courseId}.dat"; 98 | await this.SendFile(_gsOptions.XmlResourcePath, tedFile); 99 | } 100 | 101 | private async Task OnGetList(GrimRequest request, Player player) 102 | { 103 | if (!request.TryGetParameterByKey("user_id", out var userIdParam) || userIdParam.Text.Length > 32) 104 | { 105 | _logger.LogWarning("Got course getlist without 'user_id'"); 106 | return BadRequest(); 107 | } 108 | 109 | var user = await _users.GetByPSNUserIdAsync(player.Data.PSNUserId); 110 | var courses = await _courses.GetAllCoursesOfUser(user.Id); 111 | 112 | var courseList = new CourseList(); 113 | courseList.Courses = new List(); 114 | 115 | var course = new Course() 116 | { 117 | Comment = "Track Comment", 118 | CourseId = 1001000, 119 | Height = 200, 120 | OneWay = 0, 121 | Status = 2, 122 | Straight = 200, 123 | Title = "Track Title", 124 | Theme = "scenery_andalusia", 125 | Length = 7878, 126 | OwnerId = player.Data.PSNUserId, 127 | Corners = 69, 128 | OriginalCreator = player.Data.PSNUserId, 129 | PhotoId = 12345678, 130 | UpdateTime = DateTime.Now, 131 | PhotoHidden = 0, 132 | }; 133 | 134 | courseList.Courses.Add(course); 135 | return Ok(courseList); 136 | } 137 | 138 | private async Task OnUpdateCourse(GrimRequest request, Player player) 139 | { 140 | if (!request.TryGetParameterByKey("course_id", out var courseIdParam)) 141 | { 142 | _logger.LogWarning("Got course getlist without 'course_id'"); 143 | return BadRequest(); 144 | } 145 | 146 | if (!request.TryGetParameterByKey("status", out var statusParam)) 147 | { 148 | _logger.LogWarning("Got course getlist without 'status'"); 149 | return BadRequest(); 150 | } 151 | 152 | return Ok(); 153 | } 154 | } 155 | } -------------------------------------------------------------------------------- /GTGrimServer/Database/Tables/UserDTO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | namespace GTGrimServer.Database.Tables 7 | { 8 | public class UserDTO 9 | { 10 | /// 11 | /// Internal Database Id. 12 | /// 13 | public int Id { get; set; } 14 | 15 | /// 16 | /// PSN Name of the user. 17 | /// 18 | public string PSNUserId { get; set; } 19 | 20 | /// 21 | /// Nickname of the user. 22 | /// 23 | public string Nickname { get; set; } 24 | 25 | /// 26 | /// IP Address of the user. 27 | /// 28 | public string IPAddress { get; set; } 29 | 30 | /// 31 | /// Country of the user. 32 | /// 33 | public string Country { get; set; } 34 | 35 | /// 36 | /// Mac address of the user. 37 | /// 38 | public string MacAddress { get; set; } 39 | 40 | /// 41 | /// A-Spec Level of the user (GT5 only). 42 | /// 43 | public int ASpecLevel { get; set; } 44 | 45 | /// 46 | /// A-Spec XP of the user (GT5 only). 47 | /// 48 | public int ASpecExp { get; set; } 49 | 50 | /// 51 | /// B-Spec Level of the user (GT5 only). 52 | /// 53 | public int BSpecLevel { get; set; } 54 | 55 | /// 56 | /// B-Spec XP of the user (GT5 only). 57 | /// 58 | public int BSpecExp { get; set; } 59 | 60 | /// 61 | /// Achievement count of the user. 62 | /// 63 | public int AchievementCount { get; set; } 64 | 65 | /// 66 | /// Last registered cash of the user. 67 | /// 68 | public int Credit { get; set; } 69 | 70 | /// 71 | /// Races won count. 72 | /// 73 | public int WinCount { get; set; } 74 | 75 | /// 76 | /// Garage car count. 77 | /// 78 | public int CarCount { get; set; } 79 | 80 | /// 81 | /// Event trophy count. 82 | /// 83 | public int TrophyCount { get; set; } 84 | 85 | /// 86 | /// Total distance driven. 87 | /// 88 | public float Odometer { get; set; } 89 | 90 | /// 91 | /// Current license of the user. 92 | /// 93 | public int LicenseLevel { get; set; } 94 | 95 | /// 96 | /// Total golded licenses. 97 | /// 98 | public int LicenseGoldCount { get; set; } 99 | 100 | /// 101 | /// Total silvered licenses. 102 | /// 103 | public int LicenseSilverCount { get; set; } 104 | 105 | /// 106 | /// Total bronzed licenses. 107 | /// 108 | public int LicenseBronzeCount { get; set; } 109 | 110 | /// 111 | /// Color Id of the helmet that the user is wearing. 112 | /// 113 | public int HelmetId { get; set; } 114 | 115 | /// 116 | /// Id of the helmet color that the user is using. 117 | /// 118 | public int HelmetColorId { get; set; } 119 | 120 | /// 121 | /// Id of the suit color that the user is wearing. 122 | /// 123 | public int WearId { get; set; } 124 | 125 | /// 126 | /// Color Id of the suit that the user is wearing. 127 | /// 128 | public int WearColorId { get; set; } 129 | 130 | /// 131 | /// Profile/Greeting (GT5) comment. 132 | /// 133 | public string Comment { get; set; } 134 | 135 | /// 136 | /// Id of the avatar for the player. 137 | /// 138 | public long AvatarPhotoId { get; set; } 139 | 140 | /// 141 | /// Unknown. 142 | /// 143 | public int BandTest { get; set; } 144 | 145 | /// 146 | /// Unknown. 147 | /// 148 | public int BandUp { get; set; } 149 | 150 | /// 151 | /// Unknown. 152 | /// 153 | public int BandDown { get; set; } 154 | 155 | /// 156 | /// Unknown. 157 | /// 158 | public DateTime BandUpdateTime { get; set; } 159 | 160 | /// 161 | /// For GT5. Menu design matiere index. 162 | /// 163 | public int MenuMatiere { get; set; } 164 | 165 | /// 166 | /// For GT5. Menu color index. 167 | /// 168 | public int MenuColor { get; set; } 169 | 170 | /// 171 | /// 0 = "Usual playing hours" visible to public, 1 = friends only. (GT5) 172 | /// 173 | public int PlaytimeLevel { get; set; } 174 | 175 | /// 176 | /// 0 = Share with friends buttons available, 1 = friends only. (GT5) 177 | /// 178 | public int ProfileLevel { get; set; } 179 | 180 | /// 181 | /// 0 = Comment visible to public, 1 = friends only. (GT5) 182 | /// 183 | public int CommentLevel { get; set; } 184 | 185 | /// 186 | /// "Usual playing hours". GT5 only. 187 | /// 188 | public string Playtime { get; set; } 189 | 190 | /// 191 | /// GT6 only (?). Current category tag. Refer to [CommunityTag|CATEGORY_*] in RText for an overview. 192 | /// 193 | public int Tag { get; set; } 194 | 195 | /// 196 | /// Unknown. 197 | /// 198 | public string WelcomeMessage { get; set; } 199 | 200 | /// 201 | /// Remaining nickname changes available. 202 | /// 203 | public int NicknameChanges { get; set; } = DefaultNicknameChangeCount; 204 | public const int DefaultNicknameChangeCount = 3; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /GTGrimServer/Models/Xml/UserProfile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | 6 | using System.Xml; 7 | using System.Xml.Serialization; 8 | 9 | using GTGrimServer.Database.Tables; 10 | using GTGrimServer.Utils; 11 | 12 | namespace GTGrimServer.Models.Xml 13 | { 14 | [XmlRoot("user")] 15 | public class UserProfile 16 | { 17 | /// 18 | /// PSN User Id of the profile. 19 | /// 20 | [XmlElement("id")] 21 | public string UserId { get; set; } 22 | 23 | [XmlElement("number")] 24 | public long Number { get; set; } 25 | 26 | [XmlElement("comment")] 27 | public string Comment { get; set; } 28 | 29 | [XmlElement("nickname")] 30 | public string Nickname { get; set; } 31 | 32 | [XmlElement("gt_friend_list")] 33 | public string GTFriendList { get; set; } 34 | 35 | [XmlElement("photo_avatar")] 36 | public long PhotoAvatar { get; set; } 37 | 38 | [XmlElement("photo_bg")] 39 | public string PhotoBackground { get; set; } 40 | 41 | [XmlElement("band_test")] 42 | public int BandTest { get; set; } 43 | 44 | [XmlElement("band_up")] 45 | public int BandUp { get; set; } 46 | 47 | [XmlElement("band_down")] 48 | public int BandDown { get; set; } 49 | 50 | [XmlElement("band_update_time")] 51 | public string BandUpdateTime { get; set; } 52 | 53 | [XmlElement("country")] 54 | public string Country { get; set; } 55 | 56 | [XmlElement("stats")] 57 | public byte[] Stats { get; set; } 58 | 59 | [XmlElement("menu_color")] 60 | public int MenuColor { get; set; } 61 | 62 | [XmlElement("menu_matiere")] 63 | public int MenuMatiere { get; set; } 64 | 65 | [XmlElement("helmet")] 66 | public int Helmet { get; set; } 67 | 68 | [XmlElement("helmet_color")] 69 | public int HelmetColor { get; set; } 70 | 71 | [XmlElement("menu_helmet")] 72 | public int MenuHelmet { get; set; } 73 | 74 | [XmlElement("wear")] 75 | public int Wear { get; set; } 76 | 77 | [XmlElement("wear_color")] 78 | public int WearColor { get; set; } 79 | 80 | [XmlElement("menu_suit")] 81 | public int MenuSuit { get; set; } 82 | 83 | [XmlElement("aspec_level")] 84 | public int ASpecLevel { get; set; } 85 | 86 | [XmlElement("bspec_level")] 87 | public int BSpecLevel { get; set; } 88 | 89 | [XmlElement("license_level")] 90 | public int LicenseLevel { get; set; } 91 | 92 | [XmlElement("profile_level")] 93 | public int ProfileLevel { get; set; } 94 | 95 | [XmlElement("comment_level")] 96 | public int CommentLevel { get; set; } 97 | 98 | [XmlElement("playtime_level")] 99 | public int PlaytimeLevel { get; set; } 100 | 101 | [XmlElement("playtime")] 102 | public string Playtime { get; set; } 103 | 104 | [XmlElement("welcomemessage")] 105 | public string WelcomeMessage { get; set; } 106 | 107 | [XmlElement("aspec_exp")] 108 | public int ASpecExp { get; set; } 109 | 110 | [XmlElement("bspec_exp")] 111 | public int BSpecExp { get; set; } 112 | 113 | [XmlElement("achievement")] 114 | public int Achievement { get; set; } 115 | 116 | [XmlElement("credit")] 117 | public int Credit { get; set; } 118 | 119 | [XmlElement("win_count")] 120 | public int WinCount { get; set; } 121 | 122 | [XmlElement("car_count")] 123 | public int CarCount { get; set; } 124 | 125 | [XmlElement("trophy")] 126 | public int Trophy { get; set; } 127 | 128 | [XmlElement("odometer")] 129 | public double Odometer { get; set; } 130 | 131 | [XmlElement("license_gold")] 132 | public int LicenseGold { get; set; } 133 | 134 | [XmlElement("license_silver")] 135 | public int LicenseSilver { get; set; } 136 | 137 | [XmlElement("license_bronze")] 138 | public int LicenseBronze { get; set; } 139 | 140 | [XmlElement("tag")] 141 | public int Tag { get; set; } 142 | 143 | [XmlElement("nickname_change")] 144 | public int NicknameChanges { get; set; } 145 | 146 | public static UserProfile FromDatabaseObject(UserDTO userDto) 147 | { 148 | var profile = new UserProfile(); 149 | profile.UserId = userDto.PSNUserId; 150 | profile.Number = userDto.Id; 151 | profile.Comment = userDto.Comment; 152 | profile.Nickname = userDto.Nickname; 153 | profile.PhotoAvatar = userDto.AvatarPhotoId; 154 | profile.PhotoBackground = ""; 155 | 156 | profile.BandDown = userDto.BandDown; 157 | profile.BandUp = userDto.BandUp; 158 | profile.BandTest = userDto.BandTest; 159 | profile.BandUpdateTime = userDto.BandUpdateTime.ToRfc3339String(); 160 | 161 | profile.Country = userDto.Country; 162 | profile.Playtime = userDto.Playtime; 163 | 164 | profile.MenuColor = userDto.MenuColor; 165 | profile.MenuMatiere = userDto.MenuMatiere; 166 | 167 | profile.Odometer = userDto.Odometer; 168 | profile.Credit = userDto.Credit; 169 | 170 | profile.ASpecExp = userDto.ASpecExp; 171 | profile.BSpecExp = userDto.BSpecExp; 172 | profile.ASpecLevel = userDto.ASpecLevel; 173 | profile.BSpecLevel = userDto.BSpecLevel; 174 | 175 | profile.LicenseLevel = userDto.LicenseLevel; 176 | profile.CommentLevel = userDto.CommentLevel; 177 | profile.PlaytimeLevel = userDto.PlaytimeLevel; 178 | profile.ProfileLevel = userDto.ProfileLevel; 179 | 180 | profile.LicenseGold = userDto.LicenseGoldCount; 181 | profile.LicenseSilver = userDto.LicenseSilverCount; 182 | profile.LicenseBronze = userDto.LicenseBronzeCount; 183 | profile.Achievement = userDto.AchievementCount; 184 | profile.Trophy = userDto.TrophyCount; 185 | profile.WinCount = userDto.WinCount; 186 | profile.CarCount = userDto.CarCount; 187 | profile.WelcomeMessage = userDto.WelcomeMessage; 188 | 189 | 190 | // TODO: Figure menu_suit out 191 | profile.Helmet = userDto.HelmetId; 192 | profile.HelmetColor = userDto.HelmetColorId; 193 | profile.Wear = userDto.WearId; 194 | profile.WearColor = userDto.WearColorId; 195 | 196 | profile.NicknameChanges = userDto.NicknameChanges; 197 | return profile; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /GTGrimServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.Http; 4 | using Microsoft.AspNetCore.Authentication.JwtBearer; 5 | using Microsoft.AspNetCore.Diagnostics; 6 | 7 | using Microsoft.IdentityModel.Tokens; 8 | using Microsoft.Extensions.Configuration; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | using Microsoft.Extensions.Logging; 12 | 13 | using System; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | using System.Xml; 17 | using System.Data; 18 | using System.Threading; 19 | 20 | using Npgsql; 21 | 22 | using GTGrimServer.Database.Controllers; 23 | using GTGrimServer.Utils; 24 | using GTGrimServer.Config; 25 | using GTGrimServer.Filters; 26 | using GTGrimServer.Services; 27 | 28 | namespace GTGrimServer 29 | { 30 | public class Startup 31 | { 32 | public Startup(IConfiguration configuration) 33 | { 34 | Configuration = configuration; 35 | } 36 | 37 | public IConfiguration Configuration { get; } 38 | public ILogger Logger { get; private set; } 39 | // This method gets called by the runtime. Use this method to add services to the container. 40 | public void ConfigureServices(IServiceCollection services) 41 | { 42 | Console.WriteLine("Init: Configuring service provider"); 43 | services.AddControllers(); 44 | 45 | AddJWTAuthentication(services); 46 | 47 | services.AddMvc(options => 48 | { 49 | var settings = new XmlWriterSettings() { Indent = true, IndentChars = string.Empty, NewLineChars = "\n", NewLineHandling = NewLineHandling.Replace, OmitXmlDeclaration = false }; 50 | options.OutputFormatters.Add(new XmlSerializerOutputFormatterNamespace(settings)); 51 | }).AddXmlSerializerFormatters(); 52 | 53 | 54 | // Grim Related Services 55 | services.Configure(Configuration.GetSection(GameServerOptions.GameServer)); 56 | services.AddSingleton(); 57 | 58 | services.AddTransient((sp) => new NpgsqlConnection(Configuration["Database:ConnectionString"])); 59 | services.AddSingleton(); 60 | services.AddSingleton(); 61 | services.AddSingleton(); 62 | services.AddSingleton(); 63 | services.AddSingleton(); 64 | services.AddSingleton(); 65 | services.AddSingleton(); 66 | } 67 | 68 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. 69 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger logger) 70 | { 71 | Logger = logger; 72 | Logger.LogInformation("Init: Configuring HTTP server"); 73 | 74 | try 75 | { 76 | InitDatabase(app.ApplicationServices); 77 | } 78 | catch (Exception e) 79 | { 80 | Logger.LogCritical($"Init: Unable to init database. Make sure that the SQL service is running, and a connection to it is possible."); 81 | throw; 82 | } 83 | 84 | if (env.IsDevelopment()) 85 | { 86 | app.UseDeveloperExceptionPage(); 87 | VerifyDevSecrets(); 88 | 89 | } 90 | 91 | app.UseAuthentication(); 92 | app.UseAuthorization(); 93 | 94 | app.Use(async (context, next) => 95 | { 96 | var url = context.Request.Path.Value; 97 | if (url.Contains("/init//regionlist.xml")) 98 | context.Request.Path = "/init/regionlist.xml"; 99 | 100 | await next(); 101 | }); 102 | 103 | app.UseRouting(); 104 | 105 | app.UseAuthorization(); 106 | 107 | app.UseEndpoints(endpoints => 108 | { 109 | endpoints.MapControllers(); 110 | }); 111 | 112 | app.UseStatusCodePages(async context => 113 | { 114 | if (context.HttpContext.Response.StatusCode == StatusCodes.Status404NotFound) 115 | logger.LogWarning($"Unimplemented endpoint: {context.HttpContext.Request.Path}"); 116 | }); 117 | 118 | Logger.LogInformation("Init: Done configuring host."); 119 | } 120 | 121 | public void InitDatabase(IServiceProvider services) 122 | { 123 | Dapper.DefaultTypeMap.MatchNamesWithUnderscores = true; 124 | 125 | int attempts = 3; 126 | bool initialized = false; 127 | do 128 | { 129 | try 130 | { 131 | CreateTables(services); 132 | initialized = true; 133 | break; 134 | } 135 | catch (Exception e) 136 | { 137 | attempts--; 138 | if (attempts > 0) 139 | { 140 | Logger.LogError(e, "Could not initialize database, trying {count} more times...", attempts); 141 | Thread.Sleep(TimeSpan.FromSeconds(5)); 142 | } 143 | } 144 | } while (!initialized && attempts != 0); 145 | 146 | if (!initialized) 147 | throw new Exception("Unable to initialize database."); 148 | } 149 | 150 | private void CreateTables(IServiceProvider services) 151 | { 152 | services.GetService().CreateTable(); 153 | services.GetService().CreateTable(); 154 | services.GetService().CreateTable(); 155 | services.GetService().CreateTable(); 156 | services.GetService().CreateTable(); 157 | services.GetService().CreateTable(); 158 | services.GetService().CreateTable(); 159 | } 160 | 161 | public void AddJWTAuthentication(IServiceCollection services) 162 | { 163 | var authBuilder = services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme); 164 | authBuilder.AddJwtBearer(jwt => 165 | { 166 | jwt.TokenValidationParameters = new TokenValidationParameters 167 | { 168 | ValidateIssuerSigningKey = true, 169 | IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])), 170 | ValidateIssuer = true, 171 | ValidIssuer = Configuration["Jwt:Issuer"], 172 | ValidateAudience = true, 173 | ValidAudience = Configuration["Jwt:Audience"], 174 | ValidateLifetime = true, 175 | }; 176 | 177 | jwt.Events = new JwtBearerEvents 178 | { 179 | OnMessageReceived = context => 180 | { 181 | context.Token = context.Request.Cookies["X-gt-token"]; 182 | return Task.CompletedTask; 183 | }, 184 | }; 185 | }); 186 | } 187 | 188 | private void VerifyDevSecrets() 189 | { 190 | if (string.IsNullOrEmpty(Configuration["Database:ConnectionString"])) 191 | { 192 | throw new ArgumentException("Init: Db connection string missing in user secrets. (Database:ConnectionString)"); 193 | } 194 | 195 | if (string.IsNullOrEmpty(Configuration["Jwt:Key"])) 196 | { 197 | throw new ArgumentException("Init:Jwt encryption key missing in user secrets. (Jwt:Key)"); 198 | } 199 | 200 | if (string.IsNullOrEmpty(Configuration["Jwt:Issuer"])) 201 | { 202 | throw new ArgumentException("Init:Jwt issuer missing in user secrets. (Jwt:Issuer)"); 203 | } 204 | 205 | if (string.IsNullOrEmpty(Configuration["Jwt:Audience"])) 206 | { 207 | throw new ArgumentException("Init:Jwt audience key missing in user secrets. (Jwt:Audience)"); 208 | } 209 | 210 | Logger.LogInformation("Init: User Secrets OK"); 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /GTGrimServer/Controllers/Profiles/BbsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | using Microsoft.AspNetCore.Http; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.Extensions.Options; 6 | using Microsoft.AspNetCore.Mvc.Formatters; 7 | using Microsoft.AspNetCore.Mvc.Formatters.Xml; 8 | 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Threading.Tasks; 13 | using System.IO; 14 | 15 | using GTGrimServer.Config; 16 | using GTGrimServer.Services; 17 | using GTGrimServer.Models; 18 | using GTGrimServer.Models.Xml; 19 | using GTGrimServer.Filters; 20 | using GTGrimServer.Results; 21 | using GTGrimServer.Database.Controllers; 22 | using GTGrimServer.Database.Tables; 23 | 24 | namespace GTGrimServer.Controllers 25 | { 26 | /// 27 | /// GT5 handler for community message boards. 28 | /// 29 | [ApiController] 30 | [PDIClient] 31 | [Authorize] 32 | [Route("/ap/[controller]")] 33 | [Produces("application/xml")] 34 | public class BbsController : GrimControllerBase 35 | { 36 | private readonly ILogger _logger; 37 | private readonly GameServerOptions _gsOptions; 38 | 39 | private readonly UserDBManager _userDb; 40 | private readonly BbsBoardDBManager _bbsDb; 41 | 42 | public BbsController(PlayerManager players, 43 | UserDBManager userDb, 44 | BbsBoardDBManager bbsDb, 45 | IOptions options, ILogger logger) 46 | : base(players) 47 | { 48 | _logger = logger; 49 | _gsOptions = options.Value; 50 | 51 | _userDb = userDb; 52 | _bbsDb = bbsDb; 53 | } 54 | 55 | [HttpPost] 56 | public async Task Post() 57 | { 58 | var player = Player; 59 | if (player is null) 60 | { 61 | _logger.LogWarning("Could not get current player for host {host}", Request.Host); 62 | return Unauthorized(); 63 | } 64 | 65 | if (_gsOptions.GameType != GameType.GT5) 66 | { 67 | _logger.LogWarning("Got a bbs request on a non GT5 server from host: {host}", Request.Host); 68 | return Unauthorized(); 69 | } 70 | 71 | GrimRequest gRequest = await GrimRequest.Deserialize(Request.Body); 72 | if (gRequest is null) 73 | { 74 | // Handle 75 | var badReq = GrimResult.FromInt(-1); 76 | return BadRequest(badReq); 77 | } 78 | 79 | _logger.LogDebug("<- {command}", gRequest.Command); 80 | 81 | switch (gRequest.Command) 82 | { 83 | case "bbs.getCommentList": 84 | return await OnRequestGetCommentList(player, gRequest); 85 | case "bbs.updateComment": 86 | return await OnRequestUpdateComment(player, gRequest); 87 | case "bbs.deleteComment": 88 | return await OnRequestDeleteComment(player, gRequest); 89 | } 90 | 91 | _logger.LogDebug("Received unimplemented bbs call: {command}", gRequest.Command); 92 | var res = GrimResult.FromInt(-1); 93 | return BadRequest(res); 94 | } 95 | 96 | /// 97 | /// Fired when the player requests a message board. 98 | /// 99 | /// 100 | /// 101 | /// 102 | private async Task OnRequestGetCommentList(Player player, GrimRequest request) 103 | { 104 | if (!request.TryGetParameterByKey("bbs_board_id", out var bbsBoardIdParam) || !int.TryParse(bbsBoardIdParam.Text, out int bbs_board_id)) 105 | { 106 | _logger.LogWarning($"Got bbs.getCommentList request with missing 'bbs_board_id' parameter"); 107 | return BadRequest(); 108 | } 109 | 110 | if (!request.TryGetParameterByKey("bbs_comment_id", out var commentIdParam) || !int.TryParse(commentIdParam.Text, out int bbs_comment_id)) 111 | { 112 | _logger.LogWarning($"Got bbs.getCommentList request with missing 'bbs_comment_id' parameter"); 113 | return BadRequest(); 114 | } 115 | 116 | // Bbs Board ids is just the user number - get the user using it 117 | var user = await _userDb.GetByIDAsync(bbs_board_id); 118 | if (user is null) 119 | BadRequest(); 120 | 121 | var entries = await _bbsDb.GetAllCommentsOfBoard(bbs_board_id); 122 | var commentList = new BbsCommentList(); 123 | 124 | foreach (var entry in entries) 125 | { 126 | if (entry.Id <= bbs_comment_id) 127 | continue; 128 | 129 | string creatorUserId = player.Data.PSNUserId; 130 | if (entry.AuthorId != player.Data.Id) 131 | { 132 | creatorUserId = await _userDb.GetPSNNameByIdAsync(entry.AuthorId); 133 | if (creatorUserId is null) 134 | break; 135 | } 136 | 137 | BbsComment comment = new BbsComment(); 138 | comment.BoardId = bbs_board_id; 139 | comment.Comment = entry.Comment; 140 | comment.CreateTime = entry.CreateTime; 141 | comment.Nickname = creatorUserId; 142 | comment.UserId = creatorUserId; 143 | comment.CommentId = entry.Id; 144 | commentList.Comments.Add(comment); 145 | } 146 | 147 | return Ok(commentList); 148 | } 149 | 150 | /// 151 | /// Fired when the player posts a new comment. 152 | /// 153 | /// 154 | /// 155 | /// 156 | private async Task OnRequestUpdateComment(Player player, GrimRequest request) 157 | { 158 | if (!request.TryGetParameterByKey("bbs_board_id", out var bbsBoardIdParam) || !int.TryParse(bbsBoardIdParam.Text, out int bbs_board_id)) 159 | { 160 | _logger.LogWarning($"Got bbs.updateComment request with missing 'bbs_board_id' parameter"); 161 | return BadRequest(); 162 | } 163 | 164 | if (!request.TryGetParameterByKey("comment", out var comment)) 165 | { 166 | _logger.LogWarning($"Got bbs.updateComment request with missing 'bbs_comment_id' parameter"); 167 | return BadRequest(); 168 | } 169 | 170 | if (comment.Text.Length == 0 || comment.Text.Length > 140) 171 | { 172 | _logger.LogWarning("Got bbs.updateComment with empty or too long comment? In: {length}, Max: 30", comment.Text.Length); 173 | return BadRequest(); 174 | } 175 | 176 | // Bbs Board ids is just the user number - get the user using it 177 | var user = await _userDb.GetByIDAsync(bbs_board_id); 178 | if (user is null) 179 | BadRequest(); 180 | 181 | var newComment = new BbsDTO(bbs_board_id, player.Data.Id, comment.Text, DateTime.Now); 182 | await _bbsDb.AddAsync(newComment); 183 | 184 | return Ok(GrimResult.FromBool(true)); 185 | } 186 | 187 | /// 188 | /// Fired when the player deletes a comment from their board. 189 | /// 190 | /// 191 | /// 192 | /// 193 | private async Task OnRequestDeleteComment(Player player, GrimRequest request) 194 | { 195 | if (!request.TryGetParameterByKey("bbs_comment_id", out var commentIdParam) || !int.TryParse(commentIdParam.Text, out int bbs_comment_id)) 196 | { 197 | _logger.LogWarning($"Got bbs.deleteComment request with missing 'bbs_comment_id' parameter"); 198 | return BadRequest(); 199 | } 200 | 201 | // Get user's comment 202 | var comment = await _bbsDb.GetByIDAsync(bbs_comment_id); 203 | if (comment is null || (comment.BbsBoardId != player.Data.Id && comment.AuthorId != player.Data.Id)) 204 | return Forbid(); 205 | 206 | await _bbsDb.RemoveAsync(bbs_comment_id); 207 | return Ok(GrimResult.FromBool(true)); 208 | } 209 | } 210 | } -------------------------------------------------------------------------------- /GTGrimServer/Database/Controllers/UserDBManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Linq; 5 | using System.Threading.Tasks; 6 | 7 | using System.Data; 8 | 9 | using Microsoft.Extensions.Logging; 10 | using Dapper; 11 | using Npgsql; 12 | 13 | using Microsoft.Extensions.DependencyInjection; 14 | using GTGrimServer.Database.Tables; 15 | 16 | namespace GTGrimServer.Database.Controllers 17 | { 18 | public class UserDBManager : IDBManager 19 | { 20 | private ILogger _logger; 21 | protected IDbConnection _con; 22 | 23 | public UserDBManager(ILogger logger, IDbConnection con) 24 | { 25 | _logger = logger; 26 | _con = con; 27 | } 28 | 29 | public async Task GetByIDAsync(long id) 30 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM users WHERE id = @id", new { Id = id }); 31 | 32 | /// 33 | /// Gets an user by PSN User Id. 34 | /// 35 | /// PSN User Id of the user. 36 | /// 37 | public async Task GetByPSNUserIdAsync(string psnId) 38 | => await _con.QueryFirstOrDefaultAsync(@"SELECT * FROM users WHERE psn_user_id = @UserId", new { UserId = psnId }); 39 | 40 | /// 41 | /// Gets the internal database id of a psn user id. 42 | /// 43 | /// PSN User Id of the user. 44 | /// 45 | public async Task GetInternalIdOfUserAsync(string psnId) 46 | => await _con.QueryFirstOrDefaultAsync(@"SELECT id FROM users WHERE psn_user_id = @UserId", new { UserId = psnId }); 47 | 48 | /// 49 | /// Gets the PSN User Id of an user by DB Id. 50 | /// 51 | /// Database Id of the user. 52 | /// 53 | public async Task GetPSNNameByIdAsync(long id) 54 | => await _con.QueryFirstOrDefaultAsync(@"SELECT psn_user_id FROM users WHERE id = @id", new { Id = id }); 55 | 56 | public async Task UpdateAsync(UserDTO pData) 57 | => await _con.ExecuteAsync(@"UPDATE users", pData); 58 | 59 | /// 60 | /// Updates the user with the current nickname and available nickname changes. 61 | /// 62 | /// 63 | /// 64 | public async Task UpdateNewNickname(UserDTO pData) 65 | => await _con.ExecuteAsync(@"UPDATE users SET nickname=@Nickname, nickname_changes=@NicknameChanges WHERE id = @Id", pData); 66 | 67 | /// 68 | /// Updates the user with a new welcome message. 69 | /// 70 | /// 71 | /// 72 | public async Task UpdateWelcomeMessage(UserDTO pData) 73 | => await _con.ExecuteAsync(@"UPDATE users SET welcomemessage=@WelcomeMessage WHERE id = @Id", pData); 74 | 75 | /// 76 | /// Updates the user with helmet data. 77 | /// 78 | /// 79 | /// 80 | public async Task UpdateHelmet(UserDTO pData) 81 | => await _con.ExecuteAsync(@"UPDATE users SET helmet=@HelmetId, helmet_color=@HelmetColorId WHERE id = @Id", pData); 82 | 83 | /// 84 | /// Updates the user with wear data. 85 | /// 86 | /// 87 | /// 88 | public async Task UpdateWear(UserDTO pData) 89 | => await _con.ExecuteAsync(@"UPDATE users SET wear=@WearId, wear_color=@WearColorId WHERE id = @Id", pData); 90 | 91 | /// 92 | /// Updates the user with menu data (GT5). 93 | /// 94 | /// 95 | /// 96 | public async Task UpdateHomeDesign(UserDTO pData) 97 | => await _con.ExecuteAsync(@"UPDATE users SET menu_color=@MenuColor, menu_matiere=@MenuMatiere WHERE id = @Id", pData); 98 | 99 | /// 100 | /// Updates the user's game stats. 101 | /// 102 | /// 103 | /// 104 | public async Task UpdateGameStats(UserDTO pData) 105 | => await _con.ExecuteAsync("UPDATE users SET license_level=@LicenseLevel, achievement=@AchievementCount, trophy=@TrophyCount, " + 106 | "car_count=@CarCount, license_gold=@LicenseGoldCount, odometer=@Odometer WHERE id = @Id", pData); 107 | 108 | /// 109 | /// Updates the user's profile details. 110 | /// 111 | /// 112 | /// 113 | public async Task UpdateOnlineProfile(UserDTO pData) 114 | => await _con.ExecuteAsync("UPDATE users SET profile_level=@ProfileLevel, playtime_level=@PlaytimeLevel, comment_level=@CommentLevel, " + 115 | "playtime=@Playtime, comment=@Comment WHERE id = @Id", pData); 116 | 117 | /// 118 | /// Updates the user's home profile. 119 | /// 120 | /// 121 | /// 122 | public async Task UpdateMyHomeProfile(UserDTO pData) 123 | => await _con.ExecuteAsync("UPDATE users SET license_level=@LicenseLevel, achievement=@AchievementCount, trophy=@TrophyCount, " + 124 | "car_count=@CarCount, license_gold=@LicenseGoldCount, odometer=@Odometer, win_count=@WinCount, license_silver=@LicenseSilverCount, " + 125 | "license_bronze=@LicenseBronzeCount, aspec_level=@ASpecLevel, bspec_level=@BSpecLevel, aspec_exp=@ASpecExp, bspec_exp=@BSpecExp, " + 126 | "credit=@Credit WHERE id = @Id", pData); 127 | 128 | public async Task AddAsync(UserDTO pData) 129 | { 130 | var query = 131 | @"INSERT INTO users (psn_user_id, ipaddress, mac, country, nickname) 132 | VALUES(@PSNUserId, @IPAddress, @MacAddress, @Country, @Nickname) 133 | returning id"; 134 | 135 | return await _con.ExecuteScalarAsync(query, new { pData.PSNUserId, pData.IPAddress, 136 | pData.MacAddress, pData.Country, pData.Nickname }); 137 | } 138 | 139 | public async Task RemoveAsync(long id) 140 | => await _con.ExecuteAsync(@"DELETE FROM users WHERE id=@Id", new { Id = id }); 141 | 142 | public async Task RemoveByPSNUserIdAsync(string psnUserId) 143 | => await _con.ExecuteAsync(@"DELETE FROM users WHERE psn_user_id=@Id", new { Id = psnUserId }); 144 | 145 | public void CreateTable() 146 | { 147 | string query = 148 | @"CREATE TABLE IF NOT EXISTS users ( 149 | id SERIAL PRIMARY KEY, 150 | psn_user_id TEXT UNIQUE, 151 | ipaddress TEXT, 152 | country TEXT, 153 | mac TEXT, 154 | nickname_changes INTEGER DEFAULT 3, 155 | 156 | aspec_level INTEGER DEFAULT 0, 157 | aspec_exp INTEGER DEFAULT 0, 158 | bspec_level INTEGER DEFAULT 0, 159 | bspec_exp INTEGER DEFAULT 0, 160 | achievement INTEGER DEFAULT 0, 161 | credit BIGINT DEFAULT 0, 162 | win_count INTEGER DEFAULT 0, 163 | car_count INTEGER DEFAULT 0, 164 | trophy INTEGER DEFAULT 0, 165 | odometer REAL DEFAULT 0, 166 | license_level INTEGER DEFAULT 0, 167 | license_gold INTEGER DEFAULT 0, 168 | license_silver INTEGER DEFAULT 0, 169 | license_bronze INTEGER DEFAULT 0, 170 | 171 | helmet INTEGER DEFAULT 0, 172 | helmet_color INTEGER DEFAULT 0, 173 | 174 | wear INTEGER DEFAULT 0, 175 | wear_color INTEGER DEFAULT 0, 176 | 177 | nickname TEXT, 178 | photo_id_avatar TEXT, 179 | photo_bg TEXT, 180 | 181 | band_test INTEGER DEFAULT 0, 182 | band_up INTEGER DEFAULT 0, 183 | band_down INTEGER DEFAULT 0, 184 | band_update_time TIMESTAMP WITHOUT TIME ZONE, 185 | 186 | menu_color INTEGER DEFAULT 0, 187 | menu_matiere INTEGER DEFAULT 0, 188 | 189 | profile_level INTEGER DEFAULT 0, 190 | 191 | playtime TEXT, 192 | playtime_level INTEGER DEFAULT 0, 193 | 194 | comment TEXT, 195 | comment_level INTEGER DEFAULT 0, 196 | 197 | welcomemessage TEXT, 198 | tag INTEGER DEFAULT 0 199 | );"; 200 | 201 | _con.Execute(query); 202 | 203 | string query2 = @"CREATE INDEX IF NOT EXISTS users_psnuserid_idx ON users (psn_user_id)"; 204 | _con.Execute(query2); 205 | } 206 | } 207 | } 208 | --------------------------------------------------------------------------------