├── .gitattributes ├── .gitignore ├── README.md ├── Rice.sln ├── Rice ├── Admin.cs ├── App.config ├── Config.cs ├── Database.cs ├── Game │ ├── Area.cs │ ├── Character.cs │ ├── Item.cs │ ├── Player.cs │ ├── Quest.cs │ ├── User.cs │ └── Vehicle.cs ├── Log.cs ├── Packets.txt ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Rice.csproj ├── Server │ ├── Core │ │ ├── RiceClient.cs │ │ ├── RiceListener.cs │ │ ├── RicePacket.cs │ │ └── RiceToken.cs │ ├── Database │ │ └── Models │ │ │ ├── Character.cs │ │ │ ├── Item.cs │ │ │ ├── QuestState.cs │ │ │ ├── User.cs │ │ │ └── Vehicle.cs │ ├── PacketIO.cs │ ├── Packets │ │ ├── Area │ │ │ ├── Area.cs │ │ │ ├── Chat.cs │ │ │ └── Misc.cs │ │ ├── Auth │ │ │ └── Auth.cs │ │ ├── Game │ │ │ ├── Area.cs │ │ │ ├── Character.cs │ │ │ ├── Chase.cs │ │ │ ├── Chat.cs │ │ │ ├── Dealership.cs │ │ │ ├── Inventory.cs │ │ │ ├── Misc.cs │ │ │ ├── PartShop.cs │ │ │ ├── Party.cs │ │ │ ├── Quest.cs │ │ │ └── VisualShop.cs │ │ ├── Lobby │ │ │ ├── Misc.cs │ │ │ └── User.cs │ │ └── Static.cs │ ├── Resources.cs │ ├── RiceServer.cs │ └── Structures │ │ ├── Character.cs │ │ ├── Misc.cs │ │ ├── Resources │ │ ├── AssistTable.cs │ │ ├── ItemTable.cs │ │ ├── QuestTable.cs │ │ └── VehicleTable.cs │ │ └── Vehicle.cs ├── Utilities.cs ├── packages.config └── res │ ├── AssistClient.json │ ├── ItemClient.json │ ├── QuestClient.json │ ├── UseItemClient.json │ ├── VehicleList.json │ └── VehicleUpgrade.json └── packages └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | 158 | # some new vs dir 159 | *.vs/ 160 | 161 | #NuGet 162 | packages 163 | !packages/repositories.config 164 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rice 2 | ===== 3 | 4 | Rice is a Drift City server emulator that aims to be compatible with the latest available version of the official US client. 5 | -------------------------------------------------------------------------------- /Rice.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27703.2035 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rice", "Rice\Rice.csproj", "{251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Debug|x64 = Debug|x64 12 | Release|Any CPU = Release|Any CPU 13 | Release|x64 = Release|x64 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Debug|x64.ActiveCfg = Debug|x64 19 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Debug|x64.Build.0 = Debug|x64 20 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Release|x64.ActiveCfg = Release|x64 23 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1}.Release|x64.Build.0 = Release|x64 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {A7238F9A-C932-49F6-80EF-339C00BDDD04} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /Rice/Admin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.CodeDom; 3 | using System.Collections.Generic; 4 | using System.Dynamic; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Runtime.InteropServices; 8 | using System.Text; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Linq; 11 | using Rice.Game; 12 | using Rice.Server.Core; 13 | 14 | namespace Rice 15 | { 16 | public class Admin 17 | { 18 | private static HttpListener listener; 19 | 20 | public static void Initialize(Config config) 21 | { 22 | listener = new HttpListener(); 23 | listener.Prefixes.Add("http://localhost:8088/"); 24 | listener.Start(); 25 | 26 | listener.BeginGetContext(onGetContext, null); 27 | } 28 | 29 | private static void onGetContext(IAsyncResult result) 30 | { 31 | var ctx = listener.EndGetContext(result); 32 | switch (ctx.Request.RawUrl) 33 | { 34 | case "/status": 35 | case "/status/": 36 | onStatus(ctx); 37 | break; 38 | 39 | case "/favicon.ico": 40 | break; 41 | 42 | default: 43 | Log.WriteLine("Unhandled JSON API request: {0}", ctx.Request.RawUrl); 44 | writeJsonResp(new { status = "error", error = "Invalid API call." }, ctx); 45 | break; 46 | } 47 | listener.BeginGetContext(onGetContext, null); 48 | } 49 | 50 | private static void writeJsonResp(object obj, HttpListenerContext ctx) 51 | { 52 | var resp = ctx.Response; 53 | string str = JsonConvert.SerializeObject(obj); 54 | byte[] strBytes = Encoding.UTF8.GetBytes(str); 55 | 56 | resp.ContentLength64 = strBytes.Length; 57 | resp.OutputStream.Write(strBytes, 0, strBytes.Length); 58 | resp.OutputStream.Close(); 59 | } 60 | 61 | private static void onStatus(HttpListenerContext ctx) 62 | { 63 | dynamic status = new ExpandoObject(); 64 | status.status = "ok"; 65 | status.playercount = RiceServer.GetPlayers().Length; 66 | 67 | var servers = new [] {RiceServer.Auth, RiceServer.Lobby, RiceServer.Game, RiceServer.Area, RiceServer.Ranking}; 68 | status.servers = servers 69 | .Select(server => new 70 | { 71 | name = server.Name, 72 | clientcount = server.GetClients().Length 73 | }); 74 | 75 | status.areas = RiceServer.GetAreas() 76 | .Where(a => a.GetPlayerCount() > 0) 77 | .Select(area => new 78 | { 79 | id = area.ID, 80 | playercount = area.GetPlayerCount(), 81 | players = area.GetPlayers() 82 | .Select(p => new 83 | { 84 | name = p.ActiveCharacter.Name, 85 | level = p.ActiveCharacter.Level 86 | }) 87 | }); 88 | 89 | writeJsonResp(status, ctx); 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Rice/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Rice/Config.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace Rice 10 | { 11 | public class Config 12 | { 13 | const string configPath = "server.json"; 14 | 15 | public bool DebugMode = true; 16 | public string PublicIP = "127.0.0.1"; 17 | 18 | public ushort AuthPort = 11005; 19 | public ushort LobbyPort = 11011; 20 | public ushort GamePort = 11021; 21 | public ushort AreaPort = 11031; 22 | public ushort RankingPort = 11078; 23 | 24 | public string DatabaseHost = "localhost"; 25 | public int DatabasePort = 3306; 26 | public string DatabaseName = "RiceDB"; 27 | public string DatabaseUser = "Rice"; 28 | public string DatabasePassword = "ChangeMe"; 29 | 30 | public static Config Load(string path = "config.json") 31 | { 32 | Config config; 33 | 34 | if (File.Exists(configPath)) 35 | { 36 | Log.WriteLine("Config file exists, loading."); 37 | 38 | string json = File.ReadAllText(configPath); 39 | config = JsonConvert.DeserializeObject(json); 40 | } 41 | else 42 | { 43 | Log.WriteLine("Config file could not be found, creating."); 44 | 45 | config = new Config(); 46 | config.Save(); 47 | } 48 | 49 | return config; 50 | } 51 | 52 | public void Save() 53 | { 54 | string json = JsonConvert.SerializeObject(this, Formatting.Indented); 55 | File.WriteAllText(configPath, json); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Rice/Database.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Data.Entity; 6 | using MySql.Data.Entity; 7 | using MySql.Data.MySqlClient; 8 | using Rice.Server.Database.Models; 9 | 10 | namespace Rice 11 | { 12 | public static class DatabaseExtensions 13 | { 14 | public static DbCommand CreateTextCommand(this DbConnection dbconn, string query) 15 | { 16 | DbCommand command = dbconn.CreateCommand(); 17 | command.CommandType = CommandType.Text; 18 | command.CommandText = query; 19 | return command; 20 | } 21 | 22 | public static void AddParameter(this DbCommand command, string name, object value) 23 | { 24 | DbParameter param = command.CreateParameter(); 25 | param.ParameterName = name; 26 | param.Value = value; 27 | command.Parameters.Add(param); 28 | } 29 | } 30 | 31 | [DbConfigurationType(typeof(MySqlEFConfiguration))] 32 | public class RiceContext : DbContext 33 | { 34 | public virtual DbSet Users { get; set; } 35 | public virtual DbSet Characters { get; set; } 36 | public virtual DbSet QuestStates { get; set; } 37 | public virtual DbSet Items { get; set; } 38 | public virtual DbSet Vehicles { get; set; } 39 | 40 | public RiceContext(DbConnection connection, bool ownsConnection = false) : base(connection, ownsConnection) { } 41 | } 42 | 43 | public static class Database 44 | { 45 | static DbConnection conn; 46 | 47 | public static void Initialize(Config config) 48 | { 49 | conn = new MySqlConnection(String.Format( 50 | "Persist Security Info=True; Server={0}; Port={1}; Database={2}; Uid={3}; Pwd={4};", 51 | config.DatabaseHost, 52 | config.DatabasePort, 53 | config.DatabaseName, 54 | config.DatabaseUser, 55 | config.DatabasePassword 56 | )); 57 | } 58 | 59 | public static void Start() 60 | { 61 | Log.WriteLine("Connecting to database."); 62 | 63 | try 64 | { 65 | using (var rc = new RiceContext(conn)) 66 | { 67 | if (rc.Database.CreateIfNotExists()) 68 | { 69 | Log.WriteLine("Database did not exist, created."); 70 | 71 | // Create some test data to work with 72 | var testUser = new User 73 | { 74 | Name = "admin", 75 | PasswordHash = Utilities.MD5("admin"), 76 | CreateIP = "127.0.0.1", 77 | Status = 1, 78 | Credits = 1000 79 | }; 80 | 81 | var testChar = new Character 82 | { 83 | Name = "RiceAdmin", 84 | Mito = 12345678910, 85 | Avatar = 2, 86 | Level = 123, 87 | Experience = 123, 88 | City = 1, 89 | TID = -1 90 | }; 91 | 92 | testUser.Characters = new List {testChar}; 93 | 94 | var testVehicle = new Vehicle 95 | { 96 | CarID = 1, 97 | AuctionCount = 0, 98 | CarType = 88, 99 | Color = 0, 100 | Grade = 9, 101 | Kms = 200, 102 | Mitron = 5550f 103 | }; 104 | 105 | testChar.Vehicles = new List {testVehicle}; 106 | testChar.CurrentCarID = testVehicle.CarID; 107 | 108 | rc.Users.Add(testUser); 109 | rc.SaveChanges(); 110 | } 111 | } 112 | conn.Open(); 113 | Log.WriteLine("Connected to database."); 114 | } 115 | catch (MySqlException ex) 116 | { 117 | Log.WriteError($"Connection failed: {ex.Message}"); 118 | } 119 | } 120 | 121 | private static DbConnection GetConnection() 122 | { 123 | if (conn.State == ConnectionState.Broken) 124 | conn.Close(); 125 | if (conn.State == ConnectionState.Closed) 126 | Start(); 127 | return conn; 128 | } 129 | 130 | public static RiceContext GetContext() => new RiceContext(GetConnection()); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /Rice/Game/Area.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Rice.Server.Core; 6 | 7 | namespace Rice.Game 8 | { 9 | public class Area 10 | { 11 | public int ID; 12 | private List players; 13 | 14 | public Area(int id) 15 | { 16 | ID = id; 17 | players = new List(); 18 | } 19 | 20 | public void AddPlayer(Player player) 21 | { 22 | if (players.Contains(player)) 23 | { 24 | Log.WriteLine("Weird: Player already in area."); 25 | RemovePlayer(player); 26 | } 27 | players.Add(player); 28 | player.ActiveCharacter.Area = this; 29 | Log.WriteLine("Added player {0} to area {1}.", player.ActiveCharacter.Name, ID); 30 | } 31 | 32 | public void RemovePlayer(Player player) 33 | { 34 | players.Remove(player); 35 | player.ActiveCharacter.Area = null; 36 | Log.WriteLine("Removed player {0} from area {1}.", player.ActiveCharacter.Name, ID); 37 | } 38 | 39 | public Player[] GetPlayers() 40 | { 41 | return players.ToArray(); 42 | } 43 | 44 | public int GetPlayerCount() 45 | { 46 | return players.Count; 47 | } 48 | 49 | public void BroadcastMovement(Player player, byte[] movement) // byte[] temporarily 50 | { 51 | var move = new RicePacket(0x21D); // 114 total length 52 | move.Writer.Write(player.ActiveCharacter.CarSerial); 53 | move.Writer.Write(movement); 54 | 55 | foreach (Player areaPlayer in players) 56 | { 57 | if (areaPlayer != player) 58 | areaPlayer.AreaClient.Send(move); 59 | } 60 | } 61 | 62 | public void Broadcast(RicePacket packet, RiceClient exclude = null) 63 | { 64 | foreach (var player in GetPlayers()) 65 | { 66 | var client = player.AreaClient; 67 | 68 | if (exclude == null || client != exclude) 69 | client.Send(packet); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Rice/Game/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Linq; 5 | using Rice.Server.Core; 6 | using Rice.Server.Structures; 7 | using Rice.Server.Structures.Resources; 8 | using Models = Rice.Server.Database.Models; 9 | 10 | namespace Rice.Game 11 | { 12 | public class Character 13 | { 14 | public User User; 15 | public ulong UID; 16 | public ulong CID; 17 | 18 | public string Name; 19 | public long Mito; 20 | public int Avatar; 21 | public int Level; 22 | public long Experience; 23 | public int City; 24 | public Vector4 Position; 25 | public int PosState; 26 | public int CurrentCarID; 27 | public int GarageLevel; 28 | public long TID; 29 | 30 | public List Inventory; 31 | public List Garage; 32 | 33 | private List PendingItemMods; 34 | 35 | public Area Area; 36 | public Quest ActiveQuest; 37 | public ushort CarSerial; 38 | 39 | private Character(Models.Character dbCharacter) 40 | { 41 | CID = (ulong) dbCharacter.ID; 42 | UID = (ulong) dbCharacter.UID; 43 | Name = dbCharacter.Name; 44 | Mito = dbCharacter.Mito; 45 | Avatar = dbCharacter.Avatar; 46 | Level = dbCharacter.Level; 47 | Experience = dbCharacter.Experience; 48 | City = dbCharacter.City; 49 | Position = new Vector4(dbCharacter.PosX, dbCharacter.PosY, dbCharacter.PosZ); 50 | PosState = dbCharacter.PosState; 51 | CurrentCarID = dbCharacter.CurrentCarID; 52 | GarageLevel = dbCharacter.GarageLevel; 53 | TID = dbCharacter.TID; 54 | Inventory = dbCharacter.Items.Select(Item.FromDB).ToList(); 55 | Garage = dbCharacter.Vehicles.Select(Vehicle.FromDB).ToList(); 56 | PendingItemMods = new List(); 57 | Log.WriteDebug($"char init, {Inventory.Count} items in inv, {Garage.Count} vehicles"); 58 | } 59 | 60 | public ExpInfo GetExpInfo() 61 | { 62 | return ExpInfo.FromLevelExp(Level, Experience); 63 | } 64 | 65 | public bool SelectCar(int carId) 66 | { 67 | CurrentCarID = carId; 68 | using (var rc = Database.GetContext()) 69 | { 70 | var character = rc.Characters.Find((long)CID); 71 | character.CurrentCarID = carId; 72 | rc.SaveChanges(); 73 | return true; 74 | } 75 | } 76 | 77 | public bool GrantExperience(long exp) 78 | { 79 | Experience += exp; 80 | while (Experience > RiceServer.ExpSumTable[Level]) 81 | Level++; 82 | 83 | using (var rc = Database.GetContext()) 84 | { 85 | var ch = rc.Characters.Find((long) CID); 86 | if (ch == null) 87 | { 88 | Log.WriteError($"Could not find char {CID} for GrantMito"); 89 | return false; 90 | } 91 | 92 | ch.Experience = Experience; 93 | rc.SaveChanges(); 94 | } 95 | return true; 96 | } 97 | 98 | public bool GrantMito(ulong mito) 99 | { 100 | Mito += (long)mito; 101 | 102 | using (var rc = Database.GetContext()) 103 | { 104 | var ch = rc.Characters.Find((long)CID); 105 | if (ch == null) 106 | { 107 | Log.WriteError($"Could not find char {CID} for GrantMito"); 108 | return false; 109 | } 110 | 111 | ch.Mito = Mito; 112 | rc.SaveChanges(); 113 | } 114 | return true; 115 | } 116 | 117 | public bool SpendMito(ulong mito) 118 | { 119 | long mitoAfter = Mito - (long)mito; 120 | if (mitoAfter < 0) 121 | return false; 122 | 123 | Mito = mitoAfter; 124 | 125 | using (var rc = Database.GetContext()) 126 | { 127 | var ch = rc.Characters.Find((long)CID); 128 | if (ch == null) 129 | { 130 | Log.WriteError($"Could not find char {CID} for SpendMito"); 131 | return false; 132 | } 133 | 134 | ch.Mito = Mito; 135 | rc.SaveChanges(); 136 | } 137 | return true; 138 | } 139 | 140 | public bool GrantItem(string ID, uint StackNum, out Item resultItem) 141 | { 142 | Log.WriteLine($"Attempting to grant CID {CID} item {ID} (count {StackNum})"); 143 | 144 | // TODO: Merge stacks 145 | var item = Item.CreateEntry(CID, ID, StackNum); 146 | resultItem = item; 147 | 148 | if (item == null) 149 | return false; 150 | 151 | Inventory.Add(item); // should be auto-added to inv in db at CreateEntry 152 | addItemMod(item); 153 | return true; 154 | } 155 | 156 | public bool EquipItem(int invIdx, short destSlot, int carId) 157 | { 158 | var invItem = Inventory.SingleOrDefault(i => i.invIdx == invIdx); 159 | if (invItem == null) 160 | { 161 | Log.WriteError($"Attempted to equip non-existant item at invIdx {invIdx}"); 162 | return false; 163 | } 164 | 165 | if (Garage.SingleOrDefault(v => v.CarID == carId) == null) 166 | { 167 | Log.WriteError($"Couldn't find car id {carId} to equip item at invIdx {invIdx}"); 168 | return false; 169 | } 170 | 171 | var itemCat = invItem.itemEntry.Category; 172 | if (!ItemTable.SlotMap.ContainsKey(itemCat) || 173 | !ItemTable.SlotMap[itemCat].Contains(destSlot)) 174 | { 175 | Log.WriteError($"Attempted to equip invIdx {invIdx} to {destSlot} which is invalid for category {itemCat}"); 176 | return false; 177 | } 178 | 179 | using (var rc = Database.GetContext()) 180 | { 181 | var itemsInSlot = rc.Items.Count(i => i.CID == (long) CID && i.State == 1 && i.Slot == destSlot); 182 | if (itemsInSlot >= 3) 183 | { 184 | Log.WriteError( 185 | $"Attempted to equip invIdx {invIdx} to {destSlot} which already has {itemsInSlot} items"); 186 | return false; 187 | } 188 | 189 | var dbItem = rc.Items.SingleOrDefault(i => i.CID == (long) CID && i.InvIdx == invIdx); 190 | if (dbItem == null) 191 | { 192 | Log.WriteError($"Failed to find item invIdx {invIdx} for Character.EquipItem"); 193 | return false; 194 | } 195 | 196 | dbItem.Slot = destSlot; 197 | invItem.slot = (ushort) destSlot; 198 | 199 | dbItem.CurCarID = carId; 200 | invItem.curCarID = (uint) carId; 201 | 202 | dbItem.State = 1; 203 | invItem.state = 1; 204 | 205 | rc.SaveChanges(); 206 | addItemMod(invItem, true); 207 | } 208 | 209 | return true; 210 | } 211 | 212 | public bool UnEquipItem(int invIdx, int carId) 213 | { 214 | var invItem = Inventory.SingleOrDefault(i => i.invIdx == invIdx); 215 | if (invItem == null) 216 | { 217 | Log.WriteError($"Attempted to unequip non-existant item at invIdx {invIdx}"); 218 | return false; 219 | } 220 | 221 | if (Garage.SingleOrDefault(v => v.CarID == carId) == null) 222 | { 223 | Log.WriteError($"Couldn't find car id {carId} to unequip item at invIdx {invIdx}"); 224 | return false; 225 | } 226 | 227 | using (var rc = Database.GetContext()) 228 | { 229 | var dbItem = rc.Items.SingleOrDefault(i => i.CID == (long)CID && i.InvIdx == invIdx && i.CurCarID == carId); 230 | if (dbItem == null) 231 | { 232 | Log.WriteError($"Failed to find item invIdx {invIdx} carId {carId} for Character.UnEquipItem"); 233 | return false; 234 | } 235 | 236 | dbItem.Slot = 0; 237 | invItem.slot = 0; 238 | 239 | dbItem.CurCarID = 0; 240 | invItem.curCarID = 0; 241 | 242 | dbItem.State = 0; 243 | invItem.state = 0; 244 | 245 | rc.SaveChanges(); 246 | addItemMod(invItem, true); 247 | } 248 | 249 | return true; 250 | } 251 | 252 | public bool GrantVehicle(int carSort, out Vehicle resultVehicle, out Item resultKeyItem, int color = 0, int grade = 1) 253 | { 254 | Log.WriteLine($"Attempting to grant CID {CID} car {carSort}"); 255 | 256 | var vehicle = Vehicle.CreateEntry(CID, carSort, color, grade); 257 | resultVehicle = vehicle; 258 | if (vehicle == null) 259 | { 260 | resultKeyItem = null; 261 | return false; 262 | } 263 | 264 | var vehicleListEntry = VehicleTable.Vehicles[carSort]; 265 | var tableKeyItem = ItemTable.Items.FirstOrDefault(i => i.Category == "car" && i.ID == vehicleListEntry.GetKeyId()); 266 | 267 | if (tableKeyItem == null) 268 | { 269 | Log.WriteError("Failed to find key in item table"); 270 | // TODO: roll back vehicle creation :| 271 | resultKeyItem = null; 272 | return false; 273 | } 274 | 275 | var keyItem = Item.CreateEntry(CID, tableKeyItem.ID, 1, carId: vehicle.CarID); 276 | resultKeyItem = keyItem; 277 | if (keyItem == null) 278 | { 279 | Log.WriteError("Failed to create key"); 280 | return false; 281 | } 282 | 283 | Garage.Add(vehicle); 284 | Inventory.Add(keyItem); 285 | addItemMod(keyItem); 286 | return true; 287 | } 288 | 289 | public bool RemoveVehicle(int carId) 290 | { 291 | var vehicle = Garage.SingleOrDefault(v => v.CarID == carId); 292 | if (vehicle == null) 293 | { 294 | Log.WriteError($"Failed to find vehicle for RemoveVehicle({carId})"); 295 | return false; 296 | } 297 | 298 | var key = Inventory.SingleOrDefault(i => i.curCarID == vehicle.CarID); 299 | if (key == null) 300 | { 301 | Log.WriteError($"Failed to find vehicle key for RemoveVehicle({carId})"); 302 | return false; 303 | } 304 | 305 | using (var rc = Database.GetContext()) 306 | { 307 | var dbVehicle = rc.Vehicles.SingleOrDefault(v => v.CID == (long) CID && v.CarID == vehicle.CarID); 308 | var dbKey = rc.Items.SingleOrDefault(i => i.CID == (long) CID && i.CurCarID == vehicle.CarID); 309 | 310 | if (dbVehicle == null || dbKey == null) 311 | { 312 | Log.WriteError($"Failed to find db counterparts of vehicle/key for RemoveVehicle({carId})"); 313 | return false; 314 | } 315 | 316 | rc.Vehicles.Remove(dbVehicle); 317 | rc.Items.Remove(dbKey); 318 | rc.SaveChanges(); 319 | 320 | Inventory.Remove(key); 321 | Garage.Remove(vehicle); 322 | 323 | key.stackNum = 0; 324 | addItemMod(key); 325 | } 326 | return true; 327 | } 328 | 329 | public bool DropItem(int invIdx, out Item resultItem, uint count = 1) 330 | { 331 | using (var rc = Database.GetContext()) 332 | { 333 | var dbItem = rc.Items.SingleOrDefault(i => i.CID == (long)CID && i.InvIdx == invIdx && i.StackNum >= count); 334 | var invItem = Inventory.SingleOrDefault(i => i.invIdx == invIdx); 335 | 336 | if (dbItem == null || invItem == null || !invItem.itemEntry.IsSellable()) 337 | { 338 | Log.WriteError($"Failed to find droppable item invIdx {invIdx} count >= {count} owned by {CID}"); 339 | resultItem = null; 340 | return false; 341 | } 342 | 343 | bool destroyItem = count == dbItem.StackNum && count == invItem.stackNum; 344 | if (destroyItem) 345 | { 346 | rc.Items.Remove(dbItem); 347 | Inventory.Remove(invItem); 348 | invItem.stackNum = 0; 349 | } 350 | else 351 | { 352 | dbItem.StackNum -= (int)count; 353 | invItem.stackNum -= count; 354 | } 355 | rc.SaveChanges(); 356 | resultItem = invItem; 357 | addItemMod(invItem); 358 | return true; 359 | } 360 | } 361 | 362 | private void addItemMod(Item item, bool moved = false) 363 | { 364 | var itemInfo = item.GetInfo(); 365 | var modInfo = new ItemModInfo 366 | { 367 | Item = itemInfo, 368 | State = moved ? 3 : itemInfo.StackNum == 0 ? 2 : 0 369 | }; 370 | PendingItemMods.Add(modInfo); 371 | } 372 | 373 | public void FlushModInfo(RiceClient client) 374 | { 375 | var mods = PendingItemMods.ToArray(); 376 | PendingItemMods.Clear(); 377 | 378 | //ItemModList 379 | var packet = new RicePacket(402); 380 | packet.Writer.Write(mods.Length); 381 | foreach (var itemMod in mods) 382 | packet.Writer.Write(itemMod); 383 | client.Send(packet); 384 | } 385 | 386 | public CharInfo GetInfo() 387 | { 388 | return new CharInfo 389 | { 390 | Avatar = (ushort)Avatar, 391 | Name = Name, 392 | CID = CID, 393 | City = City, 394 | Position = Position, 395 | PosState = PosState, 396 | CurrentCarID = CurrentCarID, 397 | PType = (byte)'A', 398 | TeamJoinDate = DateTime.Now, 399 | TeamLeaveDate = DateTime.Now, 400 | TeamCloseDate = DateTime.Now, 401 | ExpInfo = GetExpInfo(), 402 | HancoinGarage = GarageLevel, 403 | Level = (ushort)Level, 404 | TeamId = TID, 405 | TeamName = "", // load this 406 | MitoMoney = Mito, 407 | Flags = -1 408 | }; 409 | } 410 | 411 | public static Character Retrieve(string charname) 412 | { 413 | Character character; 414 | 415 | using (var rc = Database.GetContext()) 416 | character = new Character(rc.Characters.SingleOrDefault(c => c.Name == charname)); 417 | 418 | return character; 419 | } 420 | 421 | public static bool IsNameUsable(string name) 422 | { 423 | using (var rc = Database.GetContext()) 424 | return rc.Characters.Count(c => c.Name == name) == 0; 425 | } 426 | 427 | public static bool Create(ulong uid, string name, ushort avatar) 428 | { 429 | using (var rc = Database.GetContext()) 430 | { 431 | rc.Characters.Add(new Server.Database.Models.Character 432 | { 433 | UID = (long) uid, 434 | Name = name, 435 | Avatar = avatar, 436 | TID = -1 437 | }); 438 | 439 | try 440 | { 441 | rc.SaveChanges(); 442 | } 443 | catch (DataException ex) 444 | { 445 | Log.WriteError(ex.ToString()); 446 | return false; 447 | } 448 | } 449 | return true; 450 | } 451 | 452 | public static bool Delete(string name) 453 | { 454 | using (var rc = Database.GetContext()) 455 | { 456 | var ch = rc.Characters.SingleOrDefault(c => c.Name == name); 457 | if (ch == null) 458 | { 459 | Log.WriteError($"Tried to delete non-existing character named {name}"); 460 | return false; 461 | } 462 | 463 | rc.Characters.Remove(ch); 464 | 465 | try 466 | { 467 | rc.SaveChanges(); 468 | } 469 | catch (DataException ex) 470 | { 471 | Log.WriteError(ex.ToString()); 472 | return false; 473 | } 474 | return true; 475 | } 476 | } 477 | 478 | public bool SaveCarPos(int channelId, Vector4 pos, int cityId, int posState) 479 | { 480 | using (var rc = Database.GetContext()) 481 | { 482 | var ch = rc.Characters.Find((long) CID); 483 | if (ch == null) 484 | { 485 | Log.WriteError($"Could not find char {CID} for SaveCarPos"); 486 | return false; 487 | } 488 | 489 | ch.PosX = pos.X; 490 | ch.PosY = pos.Y; 491 | ch.PosZ = pos.Z; 492 | ch.City = cityId; 493 | ch.PosState = posState; 494 | rc.SaveChanges(); 495 | } 496 | return true; 497 | } 498 | 499 | public static List Retrieve(ulong uid) 500 | { 501 | using (var rc = Database.GetContext()) 502 | { 503 | var user = rc.Users.Find((long)uid); 504 | return user.Characters.Select(ch => new Character(ch)).ToList(); 505 | } 506 | } 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /Rice/Game/Item.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using Rice.Server.Structures; 3 | using Rice.Server.Structures.Resources; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data.Common; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Models = Rice.Server.Database.Models; 11 | 12 | namespace Rice.Game 13 | { 14 | public class Item 15 | { 16 | public uint curCarID; 17 | public ushort state; 18 | public ushort slot; 19 | public uint stackNum; 20 | public uint lastCarID; 21 | public uint assistA; 22 | public uint assistB; 23 | public uint assistC; 24 | public uint assistD; 25 | public uint assistE; 26 | public uint assistF; 27 | public uint assistG; 28 | public uint assistH; 29 | public uint assistI; 30 | public uint assistJ; 31 | public uint box; 32 | public uint belonging; 33 | public int upgrade; 34 | public int upgradePoint; 35 | public uint expireTick; 36 | public float itemHealth; 37 | public int tblIdx; 38 | public uint invIdx; 39 | public int random; 40 | public string ID; 41 | public ulong CID; 42 | public GenericItemEntry itemEntry; 43 | 44 | private Item(string id, Models.Item dbItem) 45 | { 46 | string lookupId = id; 47 | 48 | if (dbItem != null) 49 | { 50 | lookupId = dbItem.ItemID; 51 | CID = (ulong)dbItem.CID; 52 | curCarID = (uint)dbItem.CurCarID; 53 | state = (ushort)dbItem.State; 54 | slot = (ushort)dbItem.Slot; 55 | stackNum = (uint)dbItem.StackNum; 56 | lastCarID = (uint)dbItem.LastCarID; 57 | assistA = (uint)dbItem.AssistA; 58 | assistB = (uint)dbItem.AssistB; 59 | assistC = (uint)dbItem.AssistC; 60 | assistD = (uint)dbItem.AssistD; 61 | assistE = (uint)dbItem.AssistE; 62 | assistF = (uint)dbItem.AssistF; 63 | assistG = (uint)dbItem.AssistG; 64 | assistH = (uint)dbItem.AssistH; 65 | assistI = (uint)dbItem.AssistI; 66 | assistJ = (uint)dbItem.AssistJ; 67 | box = (uint)dbItem.Boxed; 68 | belonging = (uint)dbItem.Bound; 69 | upgrade = dbItem.UpgradeLevel; 70 | upgradePoint = dbItem.UpgradePoints; 71 | expireTick = 0; 72 | //expireTick = dbItem.ExpireTimestamp; 73 | itemHealth = dbItem.ItemHealth; 74 | invIdx = (uint)dbItem.InvIdx; 75 | random = dbItem.Random; 76 | } 77 | 78 | tblIdx = ItemTable.Items.FindIndex(i => i.ID == lookupId); 79 | if (tblIdx == -1) 80 | { 81 | itemEntry = null; 82 | return; 83 | } 84 | ID = lookupId; 85 | itemEntry = ItemTable.Items[tblIdx]; 86 | } 87 | 88 | public static Item FromDB(Models.Item dbItem) => new Item(dbItem.ItemID, dbItem); 89 | 90 | public static Item Retrieve(ulong cid, uint InvIdx) 91 | { 92 | using (var rc = Database.GetContext()) 93 | { 94 | var item = rc.Items.SingleOrDefault(i => i.CID == (long) cid && i.InvIdx == (int) InvIdx); 95 | return item != null ? new Item(item.ItemID, item) : null; 96 | } 97 | } 98 | 99 | public static List Retrieve(ulong cid) 100 | { 101 | using (var rc = Database.GetContext()) 102 | return rc.Items.Where(i => i.CID == (long) cid).ToList().Select(i => new Item(i.ItemID, i)).ToList(); 103 | } 104 | 105 | public static Item CreateEntry(ulong CID, string ID, uint StackNum, float health = 1f, int carId = 0) 106 | { 107 | var tblIdx = ItemTable.Items.FindIndex(i => i.ID == ID); 108 | if (tblIdx < 0) 109 | { 110 | Log.WriteError($"Failed to find item id {ID} for Item.CreateEntry"); 111 | return null; 112 | } 113 | 114 | using (var rc = Database.GetContext()) 115 | { 116 | var lastItem = rc.Items.Where(i => i.CID == (long) CID).OrderByDescending(i => i.InvIdx).FirstOrDefault(); 117 | 118 | var newItem = new Models.Item 119 | { 120 | CID = (long)CID, 121 | ItemID = ID, 122 | StackNum = (int)StackNum, 123 | ItemHealth = health, 124 | InvIdx = (lastItem?.InvIdx ?? 0) + 1, 125 | CurCarID = carId, 126 | LastCarID = carId 127 | }; 128 | 129 | rc.Items.Add(newItem); 130 | rc.SaveChanges(); 131 | 132 | return new Item(newItem.ItemID, newItem); 133 | } 134 | } 135 | 136 | public bool IncreaseStackNum() 137 | { 138 | using (var rc = Database.GetContext()) 139 | { 140 | var item = rc.Items.Find(invIdx); 141 | if (item == null) 142 | { 143 | Log.WriteError($"Couldn't find item invidx {invIdx} for Item.IncreaseStackNum"); 144 | return false; 145 | } 146 | 147 | ++stackNum; 148 | item.StackNum = (int)stackNum; 149 | 150 | rc.SaveChanges(); 151 | return true; 152 | } 153 | } 154 | 155 | public ItemInfo GetInfo() 156 | { 157 | return new ItemInfo 158 | { 159 | CurCarID = curCarID, 160 | State = state, 161 | Slot = slot, 162 | StackNum = stackNum, 163 | LastCarID = lastCarID, 164 | AssistA = assistA, 165 | AssistB = assistB, 166 | AssistC = assistC, 167 | AssistD = assistD, 168 | AssistE = assistE, 169 | AssistF = assistF, 170 | AssistG = assistG, 171 | AssistH = assistH, 172 | AssistI = assistI, 173 | AssistJ = assistJ, 174 | Box = box, 175 | Belonging = belonging, 176 | Upgrade = upgrade, 177 | UpgradePoint = upgradePoint, 178 | ExpireTick = expireTick, 179 | ItemHealth = itemHealth, 180 | TableIdx = (uint) tblIdx, 181 | InvenIdx = invIdx, 182 | Random = 0 183 | }; 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /Rice/Game/Player.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Game 9 | { 10 | public class Player 11 | { 12 | public User User; 13 | public List Characters; 14 | public Character ActiveCharacter; 15 | 16 | private RiceClient authClient, lobbyClient, areaClient, gameClient; 17 | public RiceClient AuthClient { get { return authClient; } set { authClient = value; authClient.Player = this; } } 18 | public RiceClient LobbyClient { get { return lobbyClient; } set { lobbyClient = value; lobbyClient.Player = this; } } 19 | public RiceClient AreaClient { get { return areaClient; } set { areaClient = value; areaClient.Player = this; } } 20 | public RiceClient GameClient { get { return gameClient; } set { gameClient = value; gameClient.Player = this; } } 21 | 22 | public Player(User user) 23 | { 24 | User = user; 25 | Characters = new List(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Rice/Game/Quest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data.Common; 4 | using System.Linq; 5 | using System.Runtime.CompilerServices; 6 | using System.Runtime.Remoting.Messaging; 7 | using System.Text; 8 | using Rice.Server; 9 | using Rice.Server.Structures; 10 | using Rice.Server.Structures.Resources; 11 | using Models = Rice.Server.Database.Models; 12 | 13 | namespace Rice.Game 14 | { 15 | public class Quest 16 | { 17 | public ulong CID; 18 | public int QID; 19 | public int State; 20 | public int Progress; 21 | public ushort FailCount; 22 | 23 | public QuestTableEntry QuestInfo; 24 | 25 | private Quest(int qid) 26 | { 27 | QID = qid; 28 | QuestInfo = QuestTable.Quests.FirstOrDefault(q => q.TableIndex == QID); 29 | } 30 | 31 | private Quest(Models.QuestState questState) 32 | { 33 | QID = questState.QID; 34 | QuestInfo = QuestTable.Quests.FirstOrDefault(q => q.TableIndex == QID); 35 | 36 | CID = (ulong) questState.CID; 37 | 38 | State = questState.State; 39 | Progress = questState.Progress; 40 | FailCount = (ushort) questState.FailCount; 41 | } 42 | 43 | public static List Retrieve(ulong cid) 44 | { 45 | using (var rc = Database.GetContext()) 46 | { 47 | var quests = rc.QuestStates.Where(q => q.CID == (long) cid).ToList(); 48 | return quests.Select(q => new Quest(q)).ToList(); 49 | } 50 | } 51 | 52 | public static Quest Retrieve(ulong cid, int qid) 53 | { 54 | using (var rc = Database.GetContext()) 55 | { 56 | var questState = rc.QuestStates.SingleOrDefault(q => q.CID == (long)cid && q.QID == qid); 57 | return questState != null ? new Quest(questState) : null; 58 | } 59 | } 60 | 61 | public static Quest CreateEntry(ulong cid, int qid) 62 | { 63 | using (var rc = Database.GetContext()) 64 | { 65 | var character = rc.Characters.Find((long) cid); 66 | if (character == null) 67 | { 68 | Log.WriteError($"Could find character {cid} for Quest.CreateEntry"); 69 | return null; 70 | } 71 | 72 | rc.QuestStates.Add(new Models.QuestState 73 | { 74 | QID = qid, 75 | Player = character, 76 | State = 0, 77 | Progress = 0, 78 | FailCount = 0 79 | }); 80 | rc.SaveChanges(); 81 | return new Quest(qid) { CID = cid }; 82 | } 83 | } 84 | 85 | public bool IncrementProgress() 86 | { 87 | using (var rc = Database.GetContext()) 88 | { 89 | var questState = rc.QuestStates.SingleOrDefault(q => q.CID == (long)CID && q.QID == QID); 90 | if (questState == null) 91 | { 92 | Log.WriteError($"Could find QuestState with CID {CID} for Quest.IncrementProgress"); 93 | return false; 94 | } 95 | 96 | questState.Progress = ++Progress; 97 | 98 | rc.SaveChanges(); 99 | return true; 100 | } 101 | } 102 | 103 | public bool SetState(int state) 104 | { 105 | using (var rc = Database.GetContext()) 106 | { 107 | var questState = rc.QuestStates.SingleOrDefault(q => q.CID == (long) CID && q.QID == QID); 108 | if (questState == null) 109 | { 110 | Log.WriteError($"Could find QuestState with CID {CID} for Quest.IncrementProgress"); 111 | return false; 112 | } 113 | 114 | State = state; 115 | questState.State = State; 116 | 117 | rc.SaveChanges(); 118 | return true; 119 | } 120 | } 121 | 122 | public bool Fail(bool gaveUp = false) 123 | { 124 | 125 | using (var rc = Database.GetContext()) 126 | { 127 | var questState = rc.QuestStates.SingleOrDefault(q => q.CID == (long) CID && q.QID == QID); 128 | if (questState == null) 129 | { 130 | Log.WriteError($"Could find QuestState with CID {CID} for Quest.IncrementProgress"); 131 | return false; 132 | } 133 | 134 | State = gaveUp ? 4 : 3; 135 | FailCount++; 136 | questState.State = State; 137 | questState.FailCount = (short)FailCount; 138 | 139 | rc.SaveChanges(); 140 | return true; 141 | } 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /Rice/Game/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Data; 4 | using System.Data.Common; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using Rice.Server.Database; 9 | using Models = Rice.Server.Database.Models; 10 | 11 | namespace Rice.Game 12 | { 13 | public enum UserStatus : byte 14 | { 15 | Invalid = 0, 16 | Normal = 1, 17 | Banned = 2, 18 | Muted = 3 19 | } 20 | 21 | public class User 22 | { 23 | public ulong UID; 24 | public string Name; 25 | public string PasswordHash; 26 | public UserStatus Status; 27 | public string CreateIP; 28 | public uint Credits; 29 | 30 | private User(Models.User dbUser = null) 31 | { 32 | if (dbUser != null) 33 | { 34 | this.UID = (ulong)dbUser.ID; 35 | this.Name = dbUser.Name; 36 | this.PasswordHash = dbUser.PasswordHash; 37 | this.Status = (UserStatus)dbUser.Status; 38 | this.CreateIP = dbUser.CreateIP; 39 | this.Credits = (uint)dbUser.Credits; 40 | } 41 | } 42 | 43 | public static User Empty => new User { Status = UserStatus.Normal }; 44 | 45 | public static User Retrieve(string username, string passwordHash) 46 | { 47 | Log.WriteLine("Username: {0}, PasswordHash: {1}", username, passwordHash); 48 | 49 | User user; 50 | 51 | using (var rc = Database.GetContext()) 52 | user = new User(rc.Users.FirstOrDefault(u => u.Name == username && u.PasswordHash == passwordHash)); 53 | 54 | return user; 55 | } 56 | 57 | public static User Retrieve(string username) 58 | { 59 | User user; 60 | 61 | using (var rc = Database.GetContext()) 62 | user = new User(rc.Users.FirstOrDefault(u => u.Name == username)); 63 | 64 | return user; 65 | } 66 | 67 | public static User Retrieve(ulong uid) 68 | { 69 | User user; 70 | 71 | using (var rc = Database.GetContext()) 72 | user = new User(rc.Users.Find((long)uid)); 73 | 74 | return user; 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Rice/Game/Vehicle.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using Rice.Server.Structures; 3 | using Rice.Server.Structures.Resources; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Data.Common; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | using Models = Rice.Server.Database.Models; 11 | 12 | namespace Rice.Game 13 | { 14 | public class Vehicle 15 | { 16 | public uint AuctionCount; 17 | public uint Color; 18 | public int CarID; 19 | public uint CarType; 20 | public uint Grade; 21 | public float Mitron; 22 | public float Kms; 23 | public uint SlotType; 24 | 25 | public VehicleUpgradeEntry VehicleUpgradeEntry; 26 | public VehicleListEntry VehicleListEntry; 27 | 28 | private Vehicle(int id, Models.Vehicle dbVehicle = null, int grade = 1) 29 | { 30 | int lookupId = id; 31 | int lookupGrade = grade; 32 | 33 | if (dbVehicle != null) 34 | { 35 | AuctionCount = (uint)dbVehicle.AuctionCount; 36 | Color = (uint)dbVehicle.Color; 37 | CarID = dbVehicle.CarID; 38 | CarType = (uint)dbVehicle.CarType; 39 | Grade = (uint)dbVehicle.Grade; 40 | Mitron = dbVehicle.Mitron; 41 | Kms = dbVehicle.Kms; 42 | SlotType = 0; 43 | 44 | lookupId = dbVehicle.CarType; 45 | lookupGrade = dbVehicle.Grade; 46 | } 47 | 48 | VehicleUpgradeEntry = VehicleTable.GetVehicleUpgrade(lookupId, lookupGrade); 49 | VehicleListEntry = VehicleTable.Vehicles[lookupId]; 50 | } 51 | 52 | public static Vehicle FromDB(Models.Vehicle dbVehicle) => new Vehicle(dbVehicle.CarID, dbVehicle, dbVehicle.Grade); 53 | 54 | public static Vehicle CreateEntry(ulong CID, int carSort, int color = 0, int grade = 1) 55 | { 56 | using (var rc = Database.GetContext()) 57 | { 58 | try 59 | { 60 | VehicleTable.GetVehicleUpgrade(carSort, grade); 61 | } 62 | catch (Exception ex) 63 | { 64 | Log.WriteError($"Failed to get vehicle {carSort} grade {grade} for Vehicle.CreateEntry: {ex.Message}"); 65 | return null; 66 | } 67 | 68 | var lastVehicle = rc.Vehicles.Where(i => i.CID == (long)CID).OrderByDescending(i => i.CarID).FirstOrDefault(); 69 | 70 | var newVehicle = new Models.Vehicle 71 | { 72 | CID = (long)CID, 73 | CarID = (lastVehicle?.CarID ?? 0) + 1, 74 | CarType = carSort, 75 | Grade = grade, 76 | AuctionCount = 0, 77 | Color = color 78 | }; 79 | 80 | rc.Vehicles.Add(newVehicle); 81 | rc.SaveChanges(); 82 | 83 | return new Vehicle(newVehicle.CarType, newVehicle); 84 | } 85 | } 86 | 87 | public CarInfo GetInfo() 88 | { 89 | return new CarInfo 90 | { 91 | AuctionOn = false, 92 | CarUnit = new CarUnit 93 | { 94 | AuctionCnt = AuctionCount, 95 | BaseColor = 0, 96 | CarID = CarID, 97 | CarType = CarType, 98 | Grade = Grade, 99 | Mitron = Mitron, 100 | Kmh = Kms, 101 | SlotType = SlotType 102 | }, 103 | Color = Color, 104 | MitronCapacity = VehicleUpgradeEntry.MitronCapacity, 105 | MitronEfficiency = VehicleUpgradeEntry.MitronEfficiency 106 | }; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Rice/Log.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Rice 9 | { 10 | public static class Log 11 | { 12 | public static void WriteLine(string format, params object[] args) 13 | { 14 | string message = string.Format(format, args); 15 | 16 | Console.WriteLine("[{0}] {1}", DateTime.Now.ToLongTimeString(), message); 17 | } 18 | 19 | public static void WriteError(string format, params object[] args) 20 | { 21 | Console.ForegroundColor = ConsoleColor.Red; 22 | WriteLine(format, args); 23 | Console.ForegroundColor = ConsoleColor.Gray; 24 | } 25 | 26 | public static void WriteDebug(string format, params object[] args) 27 | { 28 | Console.ForegroundColor = ConsoleColor.DarkGray; 29 | WriteLine($"DEBUG: {format}", args); 30 | Console.ForegroundColor = ConsoleColor.Gray; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Rice/Packets.txt: -------------------------------------------------------------------------------- 1 | 2: Ping 2 | 3: GlobalTime 3 | 4: Latency 4 | 5: Log 5 | 6: ServerPing 6 | 7: NullPing 7 | 10: UserAuth 8 | 20: UserAuth 9 | 21: UserAuth2 10 | 24: ServerMessage 11 | 41: CheckInLobby 12 | 43: CheckOutLobby 13 | 44: ClientAuth 14 | 45: LobbyChat 15 | 46: LobbyTime 16 | 60: UserInfo 17 | 64: GetImage 18 | 67: PushImageData 19 | 71: StickerUpload 20 | 73: StickerList 21 | 75: StickerSmallImage 22 | 76: StickerReportAttach 23 | 77: StickerSearch 24 | 80: CheckCharName 25 | 82: CreateChar 26 | 84: DeleteChar 27 | 85: BuyCar 28 | 87: SellCar 29 | 89: SelectCar 30 | 91: UpgradeCar_Thread 31 | 93: StickerNameCheck 32 | 95: DeleteStickerCatalog 33 | 100: ChannelList 34 | 102: ChannelInfo 35 | 120: CheckInGame 36 | 122: CheckOutGame 37 | 123: LoadChar_Thread 38 | 125: JoinChannel 39 | 127: LeaveChannel 40 | 129: LoadDummyChar 41 | 130: GameStream 42 | 133: MailSet 43 | 134: GetMail 44 | 135: CheckEvent 45 | 140: OpenChat 46 | 141: CloseChat 47 | 142: JoinChat 48 | 144: LeaveChat 49 | 146: ChatMsg 50 | 148: Whisper 51 | 152: RecommendFriend 52 | 157: ReqCouponItem 53 | 160: CarPos 54 | 162: SaveCarPos 55 | 171: DormantAccept 56 | 180: ChaseBegin 57 | 183: ChaseEnd 58 | 185: ChaseProgress 59 | 187: ChaseHit 60 | 189: ChaseRequest 61 | 190: HuvReq 62 | 192: HuvHit 63 | 194: HuvParam 64 | 196: HuvTimeBonus 65 | 200: BattleReq 66 | 203: BattleEnd 67 | 220: RegisterChannelAdmin 68 | 228: PartyPreCheck 69 | 230: FriendList 70 | 232: FriendAddByName 71 | 235: FriendDel 72 | 239: FriendRequest 73 | 240: PartyInvite 74 | 241: PartyReject 75 | 242: PartyJoin 76 | 244: PartyLeave 77 | 245: PartyBanish 78 | 246: PartyApply 79 | 247: PartyAccept 80 | 250: UnderCityStage 81 | 252: UnderCitySuggest 82 | 253: UnderCityAgree 83 | 254: UnderCityReject 84 | 257: ExitUnderCity 85 | 260: TutorialClear 86 | 262: QuestStart 87 | 264: QuestComplete 88 | 266: QuestReward 89 | 268: QuestFail 90 | 270: QuestGiveUp 91 | 272: MyQuestList 92 | 274: QuestGoalPlace 93 | 300: JoinArea 94 | 302: LeaveArea 95 | 321: ArbeitList 96 | 323: ArbeitStart 97 | 325: ArbeitReward 98 | 329: ArbeitGiveUp 99 | 331: ArbeitGoalPlace 100 | 335: ArbeitReady 101 | 337: ArbeitGo 102 | 339: ArbeitRankList 103 | 400: ItemList 104 | 403: DropItem 105 | 405: BuyItem 106 | 407: SellItem 107 | 409: EquipItem 108 | 411: UnEquipItem 109 | 413: UpgradeItem 110 | 415: ItemUnion 111 | 417: ItemUse 112 | 419: ItemUseEx 113 | 422: PackItem 114 | 424: UnPackItem 115 | 426: ModifyItem 116 | 428: CombiItem 117 | 430: UpgradeItemDouble 118 | 432: ReplenishItem 119 | 434: CheckStat 120 | 435: RepairItem 121 | 440: RegisterRoomObserver 122 | 442: UnregisterRoomObserver 123 | 443: RoomDirectJoin 124 | 444: RoomSecureJoin 125 | 445: RoomIdDirectJoin 126 | 450: InviteeList 127 | 457: GetRoomInfo 128 | 460: RoomCreate 129 | 463: RoomList 130 | 465: RoomInvite 131 | 468: RoomJoin 132 | 469: RoomLeave 133 | 470: RoomBanish 134 | 471: ChannelRoomJoin 135 | 472: ChannelRoomSecureJoin 136 | 473: SecureJoinOBS 137 | 475: SwitchToOBS 138 | 476: SwitchToPlayer 139 | 477: SecureSwitchToPlayer 140 | 478: JoinOBS 141 | 480: LeaveOBS 142 | 485: PvpReadyState 143 | 486: PvpStartReq 144 | 488: PvpLoadingStatus 145 | 489: PvpLoadComplete 146 | 491: PvpEnd 147 | 493: PvpGate 148 | 500: TradeOpen 149 | 503: TradeProposeAck 150 | 504: TradeItemOn 151 | 506: TradeItemOff 152 | 508: TradeLock 153 | 510: TradeClose 154 | 512: TradeComplete 155 | 520: SetGameMode 156 | 540: UDPTimeSync 157 | 541: MoveVehicle 158 | 544: JumpFreeTrafficVehicle 159 | 546: MoveVehiclePlus 160 | 547: MoveFreeTrafficVehicle 161 | 549: JumpVehicle 162 | 550: RemoveVehicle 163 | 562: EnterArea 164 | 564: ExitArea 165 | 566: EnterGroup 166 | 567: ExitGroup 167 | 568: SetAreaMap 168 | 569: BlockList 169 | 570: ClearAreaMap 170 | 571: AreaChat 171 | 572: GroupCombo 172 | 573: RequestCar 173 | 575: GroupComboReset 174 | 576: ReplaceCar 175 | 580: MyAuctionList 176 | 582: AuctionOn 177 | 584: AuctionCancel 178 | 586: AuctionComplete 179 | 588: AuctionList 180 | 592: AuctionBuy 181 | 595: AuctionSearch 182 | 620: RoomModify 183 | 623: RoomChangeTeam 184 | 660: GameCharInfo 185 | 662: GameCharInfoEx 186 | 681: UDPSync 187 | 682: AreaStatus 188 | 684: AllAreaStatus 189 | 700: UDPCastTraffic 190 | 702: UDPCastTCS 191 | 704: UDPFlushCast 192 | 705: RegisterAgent 193 | 707: UDPCastTCSSignal 194 | 710: GroupTCS 195 | 711: GroupTCSSignal 196 | 712: GroupUpdate 197 | 720: ObjectUpdate 198 | 721: DriveInfoUpdate 199 | 723: FuelChargeReq 200 | 740: TrafficItemEvent 201 | 780: AreaList 202 | 782: FirstPosition 203 | 784: GetDateTime 204 | 786: QuestPosition 205 | 788: MyCityPosition 206 | 801: PlayerInfoReq 207 | 805: SetVisualItem 208 | 821: SilentMove 209 | 822: ObserveMode 210 | 830: SetTeamMark 211 | 832: GetTeamMark 212 | 834: SetTeamUrl 213 | 840: MyTeamInfo 214 | 842: CreateTeam 215 | 844: CloseTeam 216 | 848: LeaveTeam 217 | 850: DelegateTeamMaster 218 | 851: CheckTeamName 219 | 853: CancelCloseTeam 220 | 855: TeamMembers 221 | 858: CharTeamInfo 222 | 860: CBattleInfo 223 | 862: GetCBattleTime 224 | 868: CheckTeamUrl 225 | 870: JoinTeamApplyByName 226 | 871: JoinTeamApply 227 | 872: JoinTeamConfirm 228 | 873: LeaveTeamRequest 229 | 874: LeaveTeamConfirm 230 | 875: BanishTeamMember 231 | 876: RejectTeamApply 232 | 880: CBattleRoomCreate 233 | 881: CBattleRoomJoin 234 | 882: EnterCBattleChannel 235 | 883: ExitCBattleChannel 236 | 884: AssignTeamRank 237 | 900: CBattleRoomReady 238 | 903: CBattleInstance 239 | 906: CBattleSet 240 | 907: CBattleJoinOBS 241 | 908: CBattleItems 242 | 910: CBattleGetSetting 243 | 916: ObserverSupport 244 | 990: InstantRequest 245 | 992: InstantStart 246 | 994: InstantComplete 247 | 999: InstantGiveUp 248 | 1001: InstantGoalPlace 249 | 1010: UpdateCouponForVar 250 | 1011: GetCouponForVar 251 | 1013: GetCouponStampList 252 | 1015: MarkCoupon 253 | 1017: OpenCoupon 254 | 1019: GetCoupon 255 | 1080: ImageRequest 256 | 1082: ImageSave 257 | 1110: EnterRushZone 258 | 1112: ExitRushZone 259 | 1114: RushHitPoint 260 | 1118: NotifyRushHuv 261 | 1122: CastRushEvent 262 | 1123: RushObjectUpdate 263 | 1125: RequestSwitchRushHuv 264 | 1151: Mita500Get 265 | 1154: Mita500GetBuff 266 | 1162: DayMissionGetReward 267 | 1163: DayMileageGetReward 268 | 1170: DayEventGetItem 269 | 1172: DayEventComplete 270 | 1200: VisualItemList 271 | 1203: BuyVisualItem_Thread 272 | 1205: EquipVisualItem 273 | 1207: UnEquipVisualItem 274 | 1209: VisualItemUse 275 | 1211: DropVisualItem 276 | 1300: BuyHistoryList 277 | 1302: SendVisualItem_Thread 278 | 1304: GetVisualItem_Thread 279 | 1306: IsValidCharName 280 | 1311: TestPlayerAnswer 281 | 1321: LapTimeReady 282 | 1323: LapTimeGate 283 | 1324: LapTimeRecord 284 | 1350: MyStickerList 285 | 1352: AttachSticker 286 | 1354: DetachSticker 287 | 1360: EndingUserRecord 288 | 1400: GetMyHancoin_Thread 289 | 1410: NotifyDelCharacter 290 | 1411: NotifyDelCharacterAck 291 | 1412: NotifyServerIp 292 | 1413: ReportHack 293 | 1500: ItemExpireCmd 294 | 1700: MyAuctionCarList 295 | 1702: AuctionCarOn 296 | 1704: AuctionCarCancel 297 | 1706: AuctionCarComplete 298 | 1708: AuctionCarList 299 | 1710: AuctionCarBuy 300 | 1713: AuctionCarSearch 301 | 1900: SelectGuild 302 | 2000: UpdateQuickSlot 303 | 3000: UCPatrolStart 304 | 3002: UCPatrolGiveUp 305 | 3004: UCPatrolGoalPlace 306 | 3010: UCBossPatrolStart 307 | 3012: UCBossPatrolGiveUp 308 | 3014: UCBossPatrolGoalPlace 309 | 3200: CityLeave -------------------------------------------------------------------------------- /Rice/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server; 7 | using Rice.Server.Core; 8 | 9 | namespace Rice 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | Console.Title = "Rice"; 16 | Console.Clear(); 17 | 18 | Config config = Config.Load(); 19 | 20 | Database.Initialize(config); 21 | Resources.Initialize(config); 22 | RiceServer.Initialize(config); 23 | Admin.Initialize(config); 24 | 25 | Database.Start(); 26 | RiceServer.Start(); 27 | Console.ReadLine(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Rice/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Rice")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Rice")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("52637600-4de3-4e8f-a586-d0611bd4cf3a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Rice/Rice.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {251D6BE4-9A01-46BD-8B2D-C725FD5FDAD1} 8 | Exe 9 | Properties 10 | Rice 11 | Rice 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | 18 | 19 | x86 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | 28 | 29 | AnyCPU 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | true 39 | bin\x64\Debug\ 40 | DEBUG;TRACE 41 | full 42 | x64 43 | prompt 44 | MinimumRecommendedRules.ruleset 45 | 46 | 47 | bin\x64\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x64 52 | prompt 53 | MinimumRecommendedRules.ruleset 54 | 55 | 56 | 57 | ..\packages\EntityFramework.6.0.0\lib\net40\EntityFramework.dll 58 | True 59 | 60 | 61 | ..\packages\EntityFramework.6.0.0\lib\net40\EntityFramework.SqlServer.dll 62 | True 63 | 64 | 65 | ..\packages\MySql.Data.6.9.8\lib\net40\MySql.Data.dll 66 | True 67 | 68 | 69 | ..\packages\MySql.Data.Entity.6.9.8\lib\net40\MySql.Data.Entity.EF6.dll 70 | True 71 | 72 | 73 | ..\packages\Newtonsoft.Json.6.0.8\lib\net40\Newtonsoft.Json.dll 74 | True 75 | 76 | 77 | 78 | 79 | 80 | True 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | Designer 144 | 145 | 146 | 147 | PreserveNewest 148 | 149 | 150 | PreserveNewest 151 | 152 | 153 | PreserveNewest 154 | 155 | 156 | PreserveNewest 157 | 158 | 159 | PreserveNewest 160 | 161 | 162 | PreserveNewest 163 | 164 | 165 | 166 | 167 | PreserveNewest 168 | 169 | 170 | 171 | 178 | -------------------------------------------------------------------------------- /Rice/Server/Core/RiceClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.Sockets; 5 | using Rice.Game; 6 | 7 | namespace Rice.Server.Core 8 | { 9 | public class RiceClient 10 | { 11 | private TcpClient tcp; 12 | private NetworkStream ns; 13 | private RiceListener parent; 14 | 15 | private byte[] buffer; 16 | private int bytesToRead; 17 | private ushort packetLength, packetID; 18 | 19 | public bool Alive; 20 | public Player Player; 21 | 22 | public RiceClient(TcpClient tcp, RiceListener parent, bool exchangeRequired) 23 | { 24 | this.tcp = tcp; 25 | this.parent = parent; 26 | 27 | ns = tcp.GetStream(); 28 | Alive = true; 29 | 30 | try 31 | { 32 | if (exchangeRequired) 33 | { 34 | buffer = new byte[56]; 35 | bytesToRead = buffer.Length; 36 | ns.BeginRead(buffer, 0, 56, onExchange, null); 37 | } 38 | else 39 | { 40 | buffer = new byte[4]; 41 | bytesToRead = buffer.Length; 42 | ns.BeginRead(buffer, 0, 4, onHeader, null); 43 | } 44 | } 45 | catch (Exception ex) { Kill(ex); } 46 | } 47 | 48 | private void onExchange(IAsyncResult result) 49 | { 50 | try 51 | { 52 | bytesToRead -= ns.EndRead(result); 53 | if (bytesToRead > 0) 54 | { 55 | ns.BeginRead(buffer, buffer.Length - bytesToRead, bytesToRead, onExchange, null); 56 | return; 57 | } 58 | 59 | ns.Write(new byte[56], 0, 56); 60 | 61 | buffer = new byte[4]; 62 | bytesToRead = buffer.Length; 63 | ns.BeginRead(buffer, 0, 4, onHeader, null); 64 | } 65 | catch (Exception ex) { Kill(ex); } 66 | } 67 | 68 | private void onHeader(IAsyncResult result) 69 | { 70 | try 71 | { 72 | bytesToRead -= ns.EndRead(result); 73 | if (bytesToRead > 0) 74 | { 75 | ns.BeginRead(buffer, buffer.Length - bytesToRead, bytesToRead, onHeader, null); 76 | return; 77 | } 78 | 79 | packetLength = BitConverter.ToUInt16(buffer, 0); 80 | packetID = BitConverter.ToUInt16(buffer, 2); 81 | 82 | bytesToRead = packetLength - 4; 83 | buffer = new byte[bytesToRead]; 84 | ns.BeginRead(buffer, 0, bytesToRead, onData, null); 85 | } 86 | catch (Exception ex) { Kill(ex); } 87 | } 88 | 89 | private void onData(IAsyncResult result) 90 | { 91 | try 92 | { 93 | bytesToRead -= ns.EndRead(result); 94 | if (bytesToRead > 0) 95 | { 96 | ns.BeginRead(buffer, buffer.Length - bytesToRead, bytesToRead, onData, null); 97 | return; 98 | } 99 | 100 | var packet = new RicePacket(this, packetID, buffer); 101 | parent.Parse(packet); 102 | 103 | buffer = new byte[4]; 104 | bytesToRead = buffer.Length; 105 | ns.BeginRead(buffer, 0, 4, onHeader, null); 106 | } 107 | catch (Exception ex) { Kill(ex); } 108 | } 109 | 110 | public void Send(RicePacket packet) 111 | { 112 | byte[] buffer = packet.Writer.GetBuffer(); 113 | 114 | int bufferLength = buffer.Length; 115 | ushort length = (ushort)(bufferLength + 2); // Length includes itself 116 | 117 | try 118 | { 119 | ns.Write(BitConverter.GetBytes(length), 0, 2); 120 | ns.Write(buffer, 0, bufferLength); 121 | } 122 | catch (Exception ex) { Kill(ex); } 123 | } 124 | 125 | public void Error(string format, params object[] args) 126 | { 127 | var err = new RicePacket(1); 128 | err.Writer.WriteUnicode(String.Format(format, args)); 129 | Send(err); 130 | } 131 | 132 | public void Kill(Exception ex) 133 | { 134 | if (ex is SocketException || ex is IOException) 135 | { 136 | Kill(); 137 | return; 138 | } 139 | 140 | string msg = ex.Message + ": " + ex.StackTrace; 141 | if (ex.InnerException != null) 142 | { 143 | // turns out most exceptions are dumb wrappers :) 144 | msg += $"\n\nInnerException:\n{ex.InnerException.Message}: {ex.InnerException.StackTrace}"; 145 | 146 | // something something deeper 147 | if (ex.InnerException.InnerException != null) 148 | msg += $"\n\nSecond InnerException:\n{ex.InnerException.InnerException.Message}: {ex.InnerException.InnerException.StackTrace}"; 149 | } 150 | 151 | Kill(msg); 152 | } 153 | 154 | public void Kill(string reason = "") 155 | { 156 | if (!Alive) return; 157 | Alive = false; 158 | 159 | Player?.ActiveCharacter?.Area?.RemovePlayer(Player); 160 | 161 | Log.WriteLine("Killing off client. {0}", reason); 162 | tcp.Close(); 163 | } 164 | 165 | public string GetRemoteIP() 166 | { 167 | var hostStr = tcp.Client.RemoteEndPoint.ToString(); 168 | return hostStr.Substring(0, hostStr.IndexOf(':')); 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /Rice/Server/Core/RiceListener.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Net; 5 | using System.Net.Sockets; 6 | 7 | namespace Rice.Server.Core 8 | { 9 | public class RiceListener 10 | { 11 | #if DEBUG 12 | private static Dictionary debugNameDatabase; 13 | #endif 14 | 15 | private Dictionary> parsers; 16 | private List checkInParsers; 17 | private List clients; 18 | 19 | private int port; 20 | private TcpListener listener; 21 | private bool exchangeRequired; 22 | 23 | public string Name; 24 | 25 | public RiceListener(string name, int port, bool exchangeRequired = true) 26 | { 27 | #if DEBUG 28 | if (debugNameDatabase == null) 29 | { 30 | debugNameDatabase = new Dictionary(); 31 | 32 | string[] src = File.ReadAllLines("Packets.txt"); 33 | foreach (var line in src) 34 | { 35 | if (line.Length <= 3) continue; 36 | string[] lineSplit = line.Split(':'); 37 | 38 | ushort id = ushort.Parse(lineSplit[0]); 39 | 40 | debugNameDatabase[id] = lineSplit[1].Trim(); 41 | } 42 | } 43 | #endif 44 | Name = name; 45 | parsers = new Dictionary>(); 46 | checkInParsers = new List(); 47 | clients = new List(); 48 | 49 | this.port = port; 50 | this.listener = new TcpListener(IPAddress.Any, port); 51 | this.exchangeRequired = exchangeRequired; 52 | } 53 | 54 | public void Start() 55 | { 56 | listener.Start(); 57 | listener.BeginAcceptTcpClient(onAccept, this.listener); 58 | 59 | //Log.WriteLine("Started listening on {0}", port); 60 | } 61 | 62 | private void onAccept(IAsyncResult result) 63 | { 64 | var tcpClient = listener.EndAcceptTcpClient(result); 65 | var riceClient = new RiceClient(tcpClient, this, exchangeRequired); 66 | 67 | Log.WriteLine("Accepted client from {0} on {1}", tcpClient.Client.RemoteEndPoint, port); 68 | 69 | clients.Add(riceClient); 70 | listener.BeginAcceptTcpClient(onAccept, this.listener); 71 | } 72 | 73 | public void SetParser(ushort id, Action parser, bool needsCheckIn) 74 | { 75 | //Log.WriteLine("Added parser for packet {0} on {1}.", id, port); 76 | parsers[id] = parser; 77 | if (needsCheckIn) 78 | checkInParsers.Add(id); 79 | } 80 | 81 | public void Parse(RicePacket packet) 82 | { 83 | //Log.WriteLine("{0} parsing {1}.", port, packet.ID); 84 | 85 | if (parsers.ContainsKey(packet.ID)) 86 | { 87 | if (checkInParsers.Contains(packet.ID) && packet.Sender.Player == null) 88 | { 89 | Log.WriteLine("Autokilling {0} sender on {1}: CheckIn requirement not met."); 90 | packet.Sender.Kill("Forbidden"); 91 | return; 92 | } 93 | 94 | parsers[packet.ID](packet); 95 | } 96 | else 97 | { 98 | Console.ForegroundColor = ConsoleColor.DarkYellow; 99 | #if DEBUG 100 | if (debugNameDatabase.ContainsKey(packet.ID)) 101 | Log.WriteLine("Received {2} (id {0}, {0:X}) on {1}.", packet.ID, port, debugNameDatabase[packet.ID]); 102 | else 103 | Log.WriteLine("Received unknown packet (id {0}, {0:X}) on {1}.", packet.ID, port); 104 | #else 105 | Log.WriteLine("Received unknown packet (id {0}, {0:X}) on {1}.", packet.ID, port); 106 | #endif 107 | Console.ForegroundColor = ConsoleColor.Gray; 108 | } 109 | } 110 | 111 | public RiceClient[] GetClients() 112 | { 113 | return clients.ToArray(); 114 | } 115 | 116 | public void Broadcast(RicePacket packet, RiceClient exclude = null) 117 | { 118 | foreach (var client in GetClients()) 119 | { 120 | if (exclude == null || client != exclude) 121 | client.Send(packet); 122 | } 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /Rice/Server/Core/RicePacket.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Rice.Server.Core 5 | { 6 | public class RicePacket 7 | { 8 | public RiceClient Sender; 9 | 10 | public PacketWriter Writer; 11 | public PacketReader Reader; 12 | 13 | public byte[] Buffer; 14 | public ushort ID; 15 | 16 | public RicePacket(ushort id) 17 | { 18 | Writer = new PacketWriter(new MemoryStream()); 19 | ID = id; 20 | 21 | Writer.Write(id); 22 | } 23 | 24 | public RicePacket(RiceClient sender, ushort id, byte[] buffer) 25 | { 26 | Sender = sender; 27 | Buffer = buffer; 28 | ID = id; 29 | Reader = new PacketReader(new MemoryStream(Buffer)); 30 | } 31 | } 32 | 33 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)] 34 | internal sealed class RicePacketAttribute : Attribute 35 | { 36 | private readonly RiceServer.ServerType handlers; 37 | private readonly ushort id; 38 | 39 | public RicePacketAttribute(ushort id, RiceServer.ServerType handlers) 40 | { 41 | this.id = id; 42 | this.handlers = handlers; 43 | } 44 | 45 | public RiceServer.ServerType Handlers { get { return handlers; } } 46 | public ushort ID { get { return id; } } 47 | 48 | public bool CheckedIn { get; set; } 49 | } 50 | } -------------------------------------------------------------------------------- /Rice/Server/Core/RiceToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.Remoting.Messaging; 5 | using System.Text; 6 | using Rice.Game; 7 | using Rice.Server.Structures; 8 | 9 | namespace Rice.Server.Core 10 | { 11 | public abstract class RiceToken 12 | { 13 | private readonly Player originPlayer; 14 | private readonly string originIP; 15 | 16 | protected RiceToken(RiceClient client) 17 | { 18 | originPlayer = client.Player; 19 | originIP = client.GetRemoteIP(); 20 | } 21 | 22 | public bool ValidateOrigin(RiceClient client, string username = null) 23 | { 24 | if (originIP != client.GetRemoteIP()) 25 | { 26 | Log.WriteError("Failed to validate origin IP of token. Origin: {0}, Client: {1}", originIP, client.GetRemoteIP()); 27 | return false; 28 | } 29 | 30 | if (username != null) 31 | { 32 | if (originPlayer.User.Name.ToLower() == username.ToLower()) 33 | return true; 34 | 35 | Log.WriteError("Failed to validate origin Username of token. Origin: {0}, Client: {1}", originPlayer.User.Name, username); 36 | return false; 37 | } 38 | return true; 39 | } 40 | 41 | public Player GetOwner() 42 | { 43 | return originPlayer; 44 | } 45 | } 46 | 47 | public class Ticket : RiceToken, ISerializable 48 | { 49 | public uint Identifier; 50 | public Ticket(RiceClient client) : base(client) { } 51 | 52 | public void Serialize(PacketWriter pw) 53 | { 54 | pw.Write(Identifier); 55 | } 56 | } 57 | 58 | public class Serial : RiceToken, ISerializable 59 | { 60 | public ushort Identifier; 61 | public Serial(RiceClient client) : base(client) { } 62 | 63 | public void Serialize(PacketWriter pw) 64 | { 65 | pw.Write(Identifier); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Rice/Server/Database/Models/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.ComponentModel.DataAnnotations.Schema; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace Rice.Server.Database.Models 10 | { 11 | public class Character 12 | { 13 | [Key] 14 | public long ID { get; set; } 15 | 16 | [StringLength(32)] 17 | public string Name { get; set; } 18 | 19 | public long Mito { get; set; } 20 | public long Experience { get; set; } 21 | 22 | public int Avatar { get; set; } 23 | public int Level { get; set; } 24 | 25 | public int City { get; set; } 26 | 27 | public float PosX { get; set; } 28 | public float PosY { get; set; } 29 | public float PosZ { get; set; } 30 | 31 | public int PosState { get; set; } 32 | 33 | public int CurrentCarID { get; set; } 34 | public int GarageLevel { get; set; } 35 | 36 | public long UID { get; set; } 37 | [ForeignKey("UID")] 38 | public virtual User Owner { get; set; } 39 | 40 | public virtual ICollection Items { get; set; } 41 | public virtual ICollection Vehicles { get; set; } 42 | 43 | public long TID { get; set; } 44 | // TODO: Crew relation 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Rice/Server/Database/Models/Item.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Rice.Server.Database.Models 9 | { 10 | public class Item 11 | { 12 | [Key] 13 | public long ID { get; set; } 14 | 15 | [StringLength(32)] 16 | public string ItemID { get; set; } 17 | 18 | public long CID { get; set; } 19 | [ForeignKey("CID")] 20 | public virtual Character Owner { get; set; } 21 | 22 | public int CurCarID { get; set; } 23 | public int LastCarID { get; set; } 24 | 25 | public short State { get; set; } 26 | public short Slot { get; set; } 27 | public int StackNum { get; set; } 28 | 29 | public int AssistA { get; set; } 30 | public int AssistB { get; set; } 31 | public int AssistC { get; set; } 32 | public int AssistD { get; set; } 33 | public int AssistE { get; set; } 34 | public int AssistF { get; set; } 35 | public int AssistG { get; set; } 36 | public int AssistH { get; set; } 37 | public int AssistI { get; set; } 38 | public int AssistJ { get; set; } 39 | 40 | public int Boxed { get; set; } 41 | public int Bound { get; set; } 42 | public int UpgradeLevel { get; set; } 43 | public int UpgradePoints { get; set; } 44 | 45 | //public DateTime ExpireTimestamp { get; set; } 46 | public float ItemHealth { get; set; } 47 | 48 | public int InvIdx { get; set; } 49 | public int Random { get; set; } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Rice/Server/Database/Models/QuestState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Rice.Server.Database.Models 9 | { 10 | public class QuestState 11 | { 12 | [Key] 13 | public long ID { get; set; } 14 | 15 | public int QID { get; set; } 16 | 17 | public int State { get; set; } 18 | public int Progress { get; set; } 19 | 20 | public short FailCount { get; set; } 21 | 22 | public long CID { get; set; } 23 | [ForeignKey("CID")] 24 | public virtual Character Player { get; set; } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Rice/Server/Database/Models/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Rice.Server.Database.Models 9 | { 10 | public class User 11 | { 12 | [Key] 13 | public long ID { get; set; } 14 | 15 | [StringLength(32)] 16 | public string Name { get; set; } 17 | 18 | [StringLength(32)] 19 | public string PasswordHash { get; set; } 20 | 21 | public byte Status { get; set; } 22 | 23 | [StringLength(15)] 24 | public string CreateIP { get; set; } 25 | 26 | // HanCoin / GoodBoyPoints 27 | public long Credits { get; set; } 28 | 29 | public virtual ICollection Characters { get; set; } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Rice/Server/Database/Models/Vehicle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | using System.ComponentModel.DataAnnotations.Schema; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Rice.Server.Database.Models 9 | { 10 | public class Vehicle 11 | { 12 | [Key] 13 | public long ID { get; set; } 14 | 15 | public long CID { get; set; } 16 | [ForeignKey("CID")] 17 | public virtual Character Owner { get; set; } 18 | 19 | public int CarID { get; set; } 20 | public int CarType { get; set; } 21 | 22 | public int Color { get; set; } 23 | public int Grade { get; set; } 24 | public int AuctionCount { get; set; } 25 | 26 | public float Mitron { get; set; } 27 | public float Kms { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Rice/Server/PacketIO.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using Rice.Server.Structures; 5 | 6 | namespace Rice.Server 7 | { 8 | public class PacketWriter : BinaryWriter 9 | { 10 | public PacketWriter(MemoryStream stream) 11 | : base(stream, Encoding.Unicode) 12 | { 13 | } 14 | 15 | public byte[] GetBuffer() 16 | { 17 | return (BaseStream as MemoryStream).ToArray(); 18 | } 19 | 20 | public void WriteUnicode(string str, bool lengthPrefix = true) 21 | { 22 | if (str == null) 23 | str = ""; 24 | 25 | byte[] buf = Encoding.Unicode.GetBytes(str + "\0"); 26 | 27 | if (lengthPrefix) 28 | Write((ushort)str.Length); 29 | 30 | Write(buf); 31 | } 32 | 33 | public void WriteUnicodeStatic(string str, int maxLength, bool nullTerminated = false) 34 | { 35 | if (str == null) 36 | str = ""; 37 | 38 | if (str.Length > maxLength) 39 | str = str.Substring(0, maxLength); 40 | 41 | if (nullTerminated) 42 | { 43 | if (str.Length > maxLength - 1) 44 | str = str.Substring(0, maxLength - 1); 45 | str += '\0'; 46 | } 47 | 48 | byte[] stringBuf = Encoding.Unicode.GetBytes(str); 49 | 50 | byte[] buf = new byte[maxLength * 2]; 51 | Array.Copy(stringBuf, 0, buf, 0, stringBuf.Length); 52 | 53 | Write(buf); 54 | } 55 | 56 | public void WriteASCIIStatic(string str, int maxLength) 57 | { 58 | if (str == null) 59 | str = ""; 60 | 61 | if (str.Length > maxLength) 62 | str = str.Substring(0, maxLength); 63 | 64 | byte[] stringBuf = Encoding.ASCII.GetBytes(str); 65 | 66 | byte[] buf = new byte[maxLength]; 67 | Array.Copy(stringBuf, 0, buf, 0, stringBuf.Length); 68 | 69 | Write(buf); 70 | } 71 | 72 | public void Write(ISerializable structure) 73 | { 74 | structure.Serialize(this); 75 | } 76 | 77 | public void Write(DateTime date) 78 | { 79 | Write(Convert.ToUInt32((date - new DateTime(1970, 1,1)).TotalSeconds)); 80 | } 81 | } 82 | 83 | public class PacketReader : BinaryReader 84 | { 85 | public PacketReader(MemoryStream stream) 86 | : base(stream, Encoding.Unicode) 87 | { 88 | } 89 | 90 | public string ReadUnicode() 91 | { 92 | StringBuilder sb = new StringBuilder(); 93 | short val; 94 | do 95 | { 96 | val = ReadInt16(); 97 | if (val > 0) 98 | sb.Append((char)val); 99 | } 100 | while (val > 0); 101 | return sb.ToString(); 102 | } 103 | 104 | public string ReadUnicodeStatic(int maxLength) 105 | { 106 | byte[] buf = ReadBytes(maxLength * 2); 107 | string str = Encoding.Unicode.GetString(buf); 108 | 109 | if (str.Contains("\0")) 110 | str = str.Substring(0, str.IndexOf('\0')); 111 | 112 | return str; 113 | } 114 | 115 | public string ReadUnicodePrefixed() 116 | { 117 | ushort length = ReadUInt16(); 118 | return ReadUnicodeStatic(length); 119 | } 120 | 121 | public string ReadASCII() 122 | { 123 | StringBuilder sb = new StringBuilder(); 124 | byte val; 125 | do 126 | { 127 | val = ReadByte(); 128 | sb.Append((char)val); 129 | } 130 | while (val > 0); 131 | return sb.ToString(); 132 | } 133 | 134 | public string ReadASCIIStatic(int maxLength) 135 | { 136 | byte[] buf = ReadBytes(maxLength); 137 | string str = Encoding.ASCII.GetString(buf); 138 | 139 | if (str.Contains("\0")) 140 | str = str.Substring(0, str.IndexOf('\0')); 141 | 142 | return str; 143 | } 144 | 145 | public DateTime ReadDate() 146 | { 147 | return new DateTime(1970, 1, 1).AddSeconds(ReadUInt32()); 148 | } 149 | 150 | public Vector4 ReadVector4() 151 | { 152 | float x = ReadSingle(); 153 | float y = ReadSingle(); 154 | float z = ReadSingle(); 155 | float rotation = ReadSingle(); 156 | return new Vector4(x, y, z, rotation); 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /Rice/Server/Packets/Area/Area.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Rice.Game; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Server.Packets.Area 9 | { 10 | class Area 11 | { 12 | [RicePacket(562, RiceServer.ServerType.Area)] 13 | public static void EnterArea(RicePacket packet) 14 | { 15 | //TODO: Associate instance with gameserver player based on provided serial, verify, handle 16 | var serial = packet.Reader.ReadUInt16(); 17 | var name = packet.Reader.ReadUnicodeStatic(21); 18 | var area = packet.Reader.ReadInt32(); 19 | var group = packet.Reader.ReadInt32(); 20 | var localtime = packet.Reader.ReadInt32(); 21 | 22 | if (packet.Sender.Player == null) 23 | { 24 | var serverSerial = RiceServer.GetSerial(serial); 25 | 26 | if (serverSerial == null || serverSerial.GetOwner().ActiveCharacter.Name != name) 27 | { 28 | Log.WriteLine("Serial non-existent or invalid for current user."); 29 | packet.Sender.Error("water u even doin"); 30 | return; 31 | } 32 | 33 | packet.Sender.Player = serverSerial.GetOwner(); 34 | packet.Sender.Player.ActiveCharacter.CarSerial = serial; 35 | packet.Sender.Player.AreaClient = packet.Sender; 36 | } 37 | 38 | RiceServer.GetArea(area).AddPlayer(packet.Sender.Player); 39 | 40 | var ack = new RicePacket(563); 41 | ack.Writer.Write(area); 42 | ack.Writer.Write(1); //Result 43 | ack.Writer.Write(localtime); 44 | ack.Writer.Write(Environment.TickCount); 45 | ack.Writer.Write(new byte[6]); // The rest of this is null 46 | packet.Sender.Send(ack); 47 | } 48 | 49 | [RicePacket(564, RiceServer.ServerType.Area, CheckedIn = true)] 50 | public static void ExitArea(RicePacket packet) 51 | { 52 | var area = packet.Reader.ReadInt32(); 53 | RiceServer.GetArea(area).RemovePlayer(packet.Sender.Player); 54 | } 55 | 56 | [RicePacket(0x21D, RiceServer.ServerType.Area, CheckedIn = true)] 57 | public static void MoveVehicle(RicePacket packet) 58 | { 59 | packet.Reader.ReadUInt16(); // Skip serial, we'll use our local copy of it. 60 | byte[] movement = packet.Reader.ReadBytes(112); 61 | 62 | packet.Sender.Player.ActiveCharacter.Area.BroadcastMovement(packet.Sender.Player, movement); 63 | } 64 | 65 | [RicePacket(0x2AA, RiceServer.ServerType.Area)] 66 | public static void AreaStatus(RicePacket packet) 67 | { 68 | var ack = new RicePacket(0x2AB); 69 | 70 | // from ZoneServer 71 | for (int i = 0; i < 100; i++) 72 | { 73 | var area = RiceServer.GetArea(i); 74 | ack.Writer.Write(area != null ? area.GetPlayerCount() : 0); 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Area/Chat.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Rice.Server.Packets.Area 8 | { 9 | public class Chat 10 | { 11 | [RicePacket(571, RiceServer.ServerType.Area)] 12 | public static void AreaChat(RicePacket packet) 13 | { 14 | string type = packet.Reader.ReadUnicodeStatic(10); 15 | packet.Reader.ReadBytes(18 * 2); // sender 18 chars max 16 | string message = packet.Reader.ReadUnicodePrefixed(); 17 | 18 | string sender = packet.Sender.Player.ActiveCharacter.Name; 19 | 20 | var ack = new RicePacket(571); 21 | ack.Writer.WriteUnicodeStatic(type, 10); 22 | ack.Writer.WriteUnicodeStatic(sender, 18); 23 | ack.Writer.WriteUnicode(message); 24 | 25 | switch (type) 26 | { 27 | case "area": 28 | packet.Sender.Player.ActiveCharacter.Area.Broadcast(ack); 29 | break; 30 | 31 | default: 32 | Log.WriteError("Undefined chat message type."); 33 | break; 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Area/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Server.Packets.Area 9 | { 10 | public static class Misc 11 | { 12 | [RicePacket(7, RiceServer.ServerType.Area | RiceServer.ServerType.Game | RiceServer.ServerType.Lobby | RiceServer.ServerType.Auth)] 13 | public static void NullPing(RicePacket packet) { } 14 | 15 | [RicePacket(540, RiceServer.ServerType.Area)] 16 | public static void TimeSync(RicePacket packet) 17 | { 18 | var ack = new RicePacket(0x21C); 19 | ack.Writer.Write(packet.Reader.ReadSingle()); // Local time 20 | ack.Writer.Write(Environment.TickCount); 21 | packet.Sender.Send(ack); 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Auth/Auth.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Linq; 5 | using Rice.Server.Core; 6 | using Rice.Game; 7 | using System.Net; 8 | 9 | namespace Rice.Server.Packets.Auth 10 | { 11 | public static class Authentication 12 | { 13 | [RicePacket(20, RiceServer.ServerType.Auth)] 14 | public static void UserAuth(RicePacket packet) 15 | { 16 | packet.Reader.ReadInt32(); // Skip 4 bytes. 17 | var username = packet.Reader.ReadUnicodeStatic(40); 18 | var password = packet.Reader.ReadASCII(); 19 | 20 | string pwhash = Utilities.MD5(password.Substring(0, password.Length - 1)); 21 | 22 | User user = User.Retrieve(username, pwhash); 23 | 24 | if (user == null || user.Status == UserStatus.Invalid) 25 | { 26 | Log.WriteLine("Attempt to log into non-existent account or use invalid password."); 27 | 28 | var invalid = new RicePacket(22); 29 | 30 | invalid.Writer.Write(0); 31 | invalid.Writer.Write(1); 32 | invalid.Writer.Write(new byte[68]); 33 | 34 | packet.Sender.Send(invalid); 35 | return; 36 | } 37 | else if (user.Status == UserStatus.Banned) 38 | { 39 | Log.WriteLine("Attempt to log into suspended account {0}.", user.Name); 40 | 41 | packet.Sender.Error("Your account has been suspended."); 42 | packet.Sender.Kill(); 43 | return; 44 | } 45 | 46 | Player player = new Player(user); 47 | player.AuthClient = packet.Sender; 48 | 49 | var ticket = RiceServer.CreateTicket(packet.Sender); 50 | 51 | RiceServer.AddPlayer(player); 52 | 53 | var ack = new RicePacket(22); 54 | 55 | ack.Writer.Write(ticket); // Ticket 56 | ack.Writer.Write(0); // Auth Result 57 | 58 | ack.Writer.Write(Environment.TickCount); // Time 59 | ack.Writer.Write(new byte[64]); // Filler ("STicket") 60 | 61 | ack.Writer.Write((ushort)23); // ServerList ID 62 | ack.Writer.Write(1); // Server Count 63 | 64 | ack.Writer.WriteUnicodeStatic("Rice Emulator", 32); // Server Name 65 | ack.Writer.Write(1); // Server ID 66 | 67 | ack.Writer.Write((float)RiceServer.GetPlayers().Length); // Player Count 68 | ack.Writer.Write(7000f); // Max Player Count 69 | 70 | ack.Writer.Write(1); // Server State 71 | 72 | ack.Writer.Write(Environment.TickCount); // Game Update Time 73 | ack.Writer.Write(Environment.TickCount); // Lobby Update Time 74 | ack.Writer.Write(Environment.TickCount); // Area1 Update Time 75 | ack.Writer.Write(Environment.TickCount); // Area2 Update Time 76 | ack.Writer.Write(Environment.TickCount); // Ranking Update Time 77 | 78 | byte[] ip = IPAddress.Parse(RiceServer.Config.PublicIP).GetAddressBytes(); 79 | ack.Writer.Write(ip); // GameServer IP 80 | ack.Writer.Write(ip); // LobbyServer IP 81 | ack.Writer.Write(ip); // AreaServer 1 IP 82 | ack.Writer.Write(ip); // AreaServer 2 IP 83 | ack.Writer.Write(ip); // Ranking IP 84 | 85 | ack.Writer.Write(RiceServer.Config.GamePort); // GameServer Port 86 | ack.Writer.Write(RiceServer.Config.LobbyPort); // LobbyServer Port 87 | ack.Writer.Write(RiceServer.Config.AreaPort); // AreaServer 1 Port 88 | ack.Writer.Write((ushort)11031); // AreaServer 2 Port 89 | ack.Writer.Write((ushort)10701); // AreaServer 1 UDP Port 90 | ack.Writer.Write((ushort)10702); // AreaServer 2 UDP Port 91 | ack.Writer.Write(RiceServer.Config.RankingPort); // Ranking Port 92 | 93 | ack.Writer.Write((ushort)0); // what 94 | 95 | packet.Sender.Send(ack); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Area.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Rice.Server.Core; 6 | using Rice.Server.Structures; 7 | 8 | namespace Rice.Server.Packets.Game 9 | { 10 | public static class Area 11 | { 12 | [RicePacket(782, RiceServer.ServerType.Game)] 13 | public static void FirstPosition(RicePacket packet) 14 | { 15 | var ack = new RicePacket(0x30F); 16 | ack.Writer.Write(0L); 17 | ack.Writer.Write(0L); 18 | ack.Writer.Write(0L); 19 | ack.Writer.Write(3); 20 | packet.Sender.Send(ack); 21 | } 22 | 23 | [RicePacket(788, RiceServer.ServerType.Game)] 24 | public static void MyCityPosition(RicePacket packet) 25 | { 26 | Log.WriteLine(BitConverter.ToString(packet.Buffer)); 27 | 28 | int channelId = packet.Reader.ReadInt32(); 29 | 30 | var ack = new RicePacket(783); 31 | var charInfo = packet.Sender.Player.ActiveCharacter.GetInfo(); 32 | ack.Writer.Write(charInfo.City); 33 | ack.Writer.Write(channelId); 34 | 35 | ack.Writer.Write(charInfo.Position); // Position 36 | ack.Writer.Write(charInfo.PosState); // PositionState, 0 for MoonPalace introduction 37 | 38 | ack.Writer.Write((byte)0); 39 | packet.Sender.Send(ack); 40 | } 41 | 42 | [RicePacket(162, RiceServer.ServerType.Game)] 43 | public static void SaveCarPos(RicePacket packet) 44 | { 45 | var channelId = packet.Reader.ReadInt32(); 46 | var position = packet.Reader.ReadVector4(); 47 | int cityId = packet.Reader.ReadInt32(); 48 | int posState = packet.Reader.ReadInt32(); 49 | 50 | packet.Sender.Player.ActiveCharacter.SaveCarPos(channelId, position, cityId, posState); 51 | } 52 | 53 | [RicePacket(300, RiceServer.ServerType.Game)] 54 | public static void JoinArea(RicePacket packet) 55 | { 56 | var ack = new RicePacket(301); 57 | ack.Writer.Write(packet.Reader.ReadUInt32()); 58 | ack.Writer.Write(1); 59 | packet.Sender.Send(ack); 60 | } 61 | 62 | [RicePacket(780, RiceServer.ServerType.Game)] 63 | public static void AreaList(RicePacket packet) 64 | { 65 | // client calls 2 functions (not using any packet data), returns 137 * (*(_DWORD *)(pktBuf + 2) - 1) + 143; 66 | var ack = new RicePacket(781); 67 | ack.Writer.Write(1); 68 | ack.Writer.Write(new byte[137]); 69 | packet.Sender.Send(ack); 70 | } 71 | 72 | [RicePacket(3200, RiceServer.ServerType.Game)] 73 | public static void CityLeave(RicePacket packet) 74 | { 75 | var ack = new RicePacket(3201); 76 | ack.Writer.Write(1); // if ( *(_DWORD *)(pktBuf + 2) == 1 ) 77 | ack.Writer.Write(packet.Reader.ReadBytes(514)); // apparently these fuckers want their own 514 bytes back 78 | packet.Sender.Send(ack); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Server.Packets.Game 9 | { 10 | public static class Character 11 | { 12 | [RicePacket(123, RiceServer.ServerType.Game)] 13 | public static void LoadCharacter(RicePacket packet) 14 | { 15 | string characterName = packet.Reader.ReadUnicode(); 16 | packet.Sender.Player.ActiveCharacter = null; 17 | 18 | foreach(var c in packet.Sender.Player.Characters) 19 | { 20 | if (c.Name == characterName) 21 | packet.Sender.Player.ActiveCharacter = c; 22 | } 23 | 24 | if(packet.Sender.Player.ActiveCharacter == null) 25 | { 26 | Log.WriteLine("Rejecting {0} for invalid user-character combination.", packet.Sender.Player.User.Name); 27 | packet.Sender.Error("you_tried.avi"); 28 | return; 29 | } 30 | 31 | var character = packet.Sender.Player.ActiveCharacter; 32 | var ack = new RicePacket(124); 33 | 34 | var ackStruct = new Structures.LoadCharAck 35 | { 36 | CharInfo = character.GetInfo(), 37 | nCarSize = (uint)character.Garage.Count, 38 | CarInfo = character.Garage.Select(v => v.GetInfo()).ToList() 39 | }; 40 | 41 | ack.Writer.Write(ackStruct); 42 | packet.Sender.Send(ack); 43 | Log.WriteLine("Sent LoadCharAck"); 44 | 45 | var stat = new RicePacket(760); // StatUpdate 46 | for (int i = 0; i < 16; ++i) 47 | stat.Writer.Write(0); 48 | stat.Writer.Write(1000); 49 | stat.Writer.Write(9001); 50 | stat.Writer.Write(9002); 51 | stat.Writer.Write(9003); 52 | stat.Writer.Write(new byte[76]); 53 | packet.Sender.Send(stat); 54 | 55 | } 56 | 57 | [RicePacket(1200, RiceServer.ServerType.Game)] 58 | public static void VisualItemList(RicePacket packet) 59 | { 60 | var ack = new RicePacket(1801); 61 | ack.Writer.Write(262144); // ListUpdate (262144 = First packet from list queue, 262145 = sequential) 62 | ack.Writer.Write(0); // ItemNum 63 | ack.Writer.Write(new byte[120]); // Null VisualItem (120 bytes per XiStrMyVSItem) 64 | packet.Sender.Send(ack); 65 | } 66 | 67 | [RicePacket(400, RiceServer.ServerType.Game)] 68 | public static void ItemList(RicePacket packet) 69 | { 70 | byte[] testItem = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0x91, 0x00, 0x00, 0x00, 0x00, 0x80, 0xBF, 75 | 0x00, 0x00, 0x00, 0x00, 0xD9, 0x04, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 76 | var ack = new RicePacket(401); 77 | ack.Writer.Write(262144); // ListUpdate (262144 = First packet from list queue, 262145 = sequential) 78 | ack.Writer.Write(1); // ItemNum 79 | ack.Writer.Write(testItem); // Null Item (96 bytes per XiStrMyItem) 80 | packet.Sender.Send(ack); 81 | } 82 | 83 | [RicePacket(801, RiceServer.ServerType.Game)] 84 | public static void PlayerInfoReq(RicePacket packet) 85 | { 86 | var reqCnt = packet.Reader.ReadUInt32(); 87 | var serial = packet.Reader.ReadUInt16(); // No known scenarios where the requested info count is > 1 88 | // Followed by the session age of the player we are requesting info for. nplutowhy.avi 89 | 90 | foreach(var p in RiceServer.GetPlayers()) 91 | { 92 | if(p.ActiveCharacter != null && p.ActiveCharacter.CarSerial == serial) 93 | { 94 | var character = p.ActiveCharacter; 95 | var playerInfo = new Structures.PlayerInfo() 96 | { 97 | Name = character.Name, 98 | Serial = character.CarSerial, 99 | Age = 0 100 | }; 101 | var res = new RicePacket(802); 102 | res.Writer.Write(1); 103 | res.Writer.Write(playerInfo); 104 | packet.Sender.Send(res); 105 | break; 106 | } 107 | } 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Chase.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using Rice.Server.Structures; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | 8 | namespace Rice.Server.Packets.Game 9 | { 10 | /* 11 | * Disclaimer: 12 | * This entire Chase implementation is unfinished, and only meant to pass the missions. 13 | * The chases need the Traffic Agent to control them, so all of these are at a standstill for now. 14 | * It needs a Chase game-object state implementation to store with players. 15 | * In addition to that, it's also lacking party implementation, sticking strictly to single-player. 16 | * Rewards also need to be generated and verified. ChaseRequest should be implemented once these are in place. 17 | */ 18 | 19 | public static class Chase 20 | { 21 | [RicePacket(189, RiceServer.ServerType.Game, CheckedIn = true)] 22 | public static void ChaseRequest(RicePacket packet) 23 | { 24 | bool now = packet.Reader.ReadByte() > 0; 25 | var position = Vector4.Deserialize(packet.Reader); 26 | Log.WriteLine($"ChaseRequest: Now - {now} | Pos {position}"); 27 | } 28 | 29 | [RicePacket(180, RiceServer.ServerType.Game, CheckedIn = true)] 30 | public static void ChaseBegin(RicePacket packet) 31 | { 32 | int chaseType = packet.Reader.ReadInt32(); 33 | var startPos = Vector4.Deserialize(packet.Reader); 34 | int firstHuvLevel = packet.Reader.ReadInt32(); 35 | int firstHuvId = packet.Reader.ReadInt32(); 36 | int huvNum = packet.Reader.ReadInt32(); 37 | 38 | //Log.WriteLine( 39 | // $"ChaseBegin:" + 40 | // $"\n\tChaseType: {chaseType}" + 41 | // $"\n\tStartPos: {startPos}" + 42 | // $"\n\tFirst HUV Lv{firstHuvLevel} ({firstHuvId}) x{huvNum}" 43 | //); 44 | 45 | var activeQuest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 46 | 47 | var ack = new RicePacket(181); //todo 48 | ack.Writer.Write(startPos); // struct XiVec4 m_StartPos; // this+0x2 49 | ack.Writer.Write((byte)1); // char m_Accepted; // this+0x12 50 | ack.Writer.Write(180); // long m_FindTimeout; // this+0x13 51 | ack.Writer.Write(180); // long m_ArrestTimeout; // this+0x17 52 | ack.Writer.Write((ushort)huvNum); // unsigned short m_huvSize; // this+0x1B 53 | for (int i = 0; i < huvNum; i++) 54 | { 55 | ack.Writer.Write(packet.Sender.Player.ActiveCharacter.CarSerial); //unsigned short m_Serial; // this+0x0 56 | ack.Writer.Write((ushort)(20397 + i)); //unsigned short m_CarSort; // this+0x2 57 | if ((activeQuest?.QuestInfo?.HuvLevel ?? 0) <= 0) 58 | { 59 | ack.Writer.Write(firstHuvLevel); //int m_huvLevel; // this+0x4 60 | ack.Writer.Write(firstHuvId); //int m_huvId; // this+0x8 61 | } 62 | else 63 | { 64 | var info = activeQuest.QuestInfo; 65 | Log.WriteLine($"Player has active quest {info.Title}, using huv info ({info.HuvLevel}, {info.HuvID})"); 66 | ack.Writer.Write(info.HuvLevel); //int m_huvLevel; // this+0x4 67 | ack.Writer.Write(info.HuvID); //int m_huvId; // this+0x8 68 | } 69 | ack.Writer.Write(140f); //float m_Speed; // this+0xC 70 | ack.Writer.Write(300f); //float m_MaxSpeed; // this+0x10 71 | ack.Writer.Write(240f); //float m_NosAccel; // this+0x14 72 | ack.Writer.Write(3f); //float m_NosTime; // this+0x18 73 | ack.Writer.Write(3f); //float m_NosRefreshRate; // this+0x1C 74 | ack.Writer.Write(43f); //float m_Durability; // this+0x20 75 | ack.Writer.Write(2f); //float m_FrontPlayerAvoidanceRate; // this+0x24 76 | ack.Writer.Write(2f); //float m_FrontTrafficAvoidanceRate; // this+0x28 77 | ack.Writer.Write(1f); //float m_RearPlayerAvoidanceRate; // this+0x2C 78 | } 79 | packet.Sender.Send(ack); 80 | } 81 | 82 | [RicePacket(185, RiceServer.ServerType.Game, CheckedIn = true)] 83 | public static void ChaseProgress(RicePacket packet) 84 | { 85 | ushort serial = packet.Reader.ReadUInt16(); 86 | ushort targetSerial = packet.Reader.ReadUInt16(); 87 | ushort targetCarSort = packet.Reader.ReadUInt16(); 88 | int time = packet.Reader.ReadInt32(); 89 | int state = packet.Reader.ReadInt32(); 90 | string[] states = "WAIT RUN ARRESTING FAILTOCATCH ENDED".Split(' '); 91 | 92 | //Log.WriteLine( 93 | // $"ChaseProgress:" + 94 | // $"\n\tSerial: {serial}" + 95 | // $"\n\tTargetSerial: {targetSerial}" + 96 | // $"\n\tTargetSort: {targetCarSort}" + 97 | // $"\n\tTime: {time}" + 98 | // $"\n\tState: {states[state]} ({state})" 99 | //); 100 | 101 | var ack = new RicePacket(185); 102 | ack.Writer.Write(serial); 103 | ack.Writer.Write(targetSerial); 104 | ack.Writer.Write(targetCarSort); 105 | ack.Writer.Write(time); 106 | ack.Writer.Write(state); 107 | packet.Sender.Send(ack); 108 | } 109 | 110 | [RicePacket(187, RiceServer.ServerType.Game, CheckedIn = true)] 111 | public static void ChaseHit(RicePacket packet) 112 | { 113 | ushort serial = packet.Reader.ReadUInt16(); 114 | ushort targetSerial = packet.Reader.ReadUInt16(); 115 | ushort targetCarSort = packet.Reader.ReadUInt16(); 116 | float damage = packet.Reader.ReadSingle(); 117 | int time = packet.Reader.ReadInt32(); 118 | uint flags = packet.Reader.ReadUInt32(); 119 | float resultLife = packet.Reader.ReadSingle(); 120 | string name = packet.Reader.ReadUnicodeStatic(10); 121 | ushort what = packet.Reader.ReadUInt16(); 122 | ushort what2 = packet.Reader.ReadUInt16(); 123 | //Log.WriteLine( 124 | // $"ChaseHit:" + 125 | // $"\n\tSerial: {serial}" + 126 | // $"\n\tTargetSerial: {targetSerial}" + 127 | // $"\n\tTargetSort: {targetCarSort}" + 128 | // $"\n\tDamage: {damage}" + 129 | // $"\n\tTime: {time}" + 130 | // $"\n\tFlags: {flags}" + 131 | // $"\n\tResultLife: {resultLife}" + 132 | // $"\n\tName: {name}" + 133 | // $"\n\tWhat: {what}" + 134 | // $"\n\tWhat2: {what2}" 135 | //); 136 | } 137 | 138 | [RicePacket(183, RiceServer.ServerType.Game, CheckedIn = true)] 139 | public static void ChaseEnd(RicePacket packet) 140 | { 141 | ushort serial = packet.Reader.ReadUInt16(); 142 | ushort carSort = packet.Reader.ReadUInt16(); 143 | uint type = packet.Reader.ReadUInt32(); 144 | float life = packet.Reader.ReadSingle(); 145 | uint result = packet.Reader.ReadUInt32(); 146 | var pos = Vector4.Deserialize(packet.Reader); 147 | var vel = Vector4.Deserialize(packet.Reader); 148 | int time = packet.Reader.ReadInt32(); 149 | 150 | // ^ this is fucked, but it's aligned enough to work for now 151 | 152 | //Log.WriteLine( 153 | // $"ChaseEnd:" + 154 | // $"\n\tBuffer: {BitConverter.ToString(packet.Buffer)}" + 155 | // $"\n\tSerial: {serial}" + 156 | // $"\n\tCarSort: {carSort}" + 157 | // $"\n\tType: {type}" + 158 | // $"\n\tLife: {life}" + 159 | // $"\n\tResult: {result}" + 160 | // $"\n\tPosition: {pos}" + 161 | // $"\n\tVelocity: {vel}" + 162 | // $"\n\tTime: {time}" 163 | //); 164 | 165 | var ack = new RicePacket(184); // ChaseResult 166 | ack.Writer.Write(result); 167 | ack.Writer.Write(time); 168 | ack.Writer.Write((ushort)1); // unitSize - player count? 169 | ack.Writer.Write(new byte[4 * 4 + 4 * 4]); // int selfdaily[4]; int teamdaily[4]; 170 | 171 | ack.Writer.WriteUnicodeStatic(packet.Sender.Player.ActiveCharacter.Name, 0x20); 172 | ack.Writer.Write(serial); 173 | ack.Writer.Write(carSort); 174 | ack.Writer.Write(0); // deltaHuvMoney 175 | ack.Writer.Write(0); // deltaBonusMoney 176 | ack.Writer.Write(0L); // money 177 | ack.Writer.Write(0); // deltaHuvExp 178 | ack.Writer.Write(0); // deltaBonusExp 179 | ack.Writer.Write(packet.Sender.Player.ActiveCharacter.GetExpInfo()); 180 | ack.Writer.Write(packet.Sender.Player.ActiveCharacter.Level); 181 | ack.Writer.Write(1f); // point? 182 | 183 | ack.Writer.Write(0); // rewardItemCount 184 | ack.Writer.Write(0); // reward 1 185 | ack.Writer.Write(0); // reward 2 186 | packet.Sender.Send(ack); 187 | } 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Chat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Game; 7 | using Rice.Server.Core; 8 | 9 | namespace Rice.Server.Packets.Game 10 | { 11 | public static class Chat 12 | { 13 | [RicePacket(146, RiceServer.ServerType.Game)] 14 | public static void ChatMessage(RicePacket packet) 15 | { 16 | string type = packet.Reader.ReadUnicodeStatic(10); 17 | bool green = packet.Reader.ReadUInt32() == 0xFF00FF00; // ignore this, use packet.Sender.Player.User.Status 18 | string message = packet.Reader.ReadUnicodePrefixed(); 19 | 20 | string sender = packet.Sender.Player.ActiveCharacter.Name; 21 | 22 | #if DEBUG 23 | if (message.ToLower().StartsWith("gibe_plos ")) 24 | { 25 | string[] split = message.Split(' '); 26 | if (split.Length == 2) 27 | { 28 | Item item; 29 | if (packet.Sender.Player.ActiveCharacter.GrantItem(split[1], 1, out item)) 30 | { 31 | packet.Sender.Error($"Gave 1x {item.itemEntry.Name}"); 32 | Log.WriteLine($"Gave {sender} 1x {item.itemEntry.Name}"); 33 | 34 | packet.Sender.Player.ActiveCharacter.FlushModInfo(packet.Sender); 35 | return; 36 | } 37 | 38 | packet.Sender.Error($"Failed to give item"); 39 | return; 40 | } 41 | } 42 | #endif 43 | 44 | Console.WriteLine("({0}) <{1}> {2}", type, sender, message); 45 | 46 | var ack = new RicePacket(147); 47 | ack.Writer.WriteUnicodeStatic(type, 10); 48 | ack.Writer.WriteUnicodeStatic(sender, 18); 49 | ack.Writer.WriteUnicode(message); 50 | 51 | switch (type) 52 | { 53 | case "room": 54 | RiceServer.Game.Broadcast(ack); // TODO: broadcast only to users in same area 55 | break; 56 | 57 | case "channel": 58 | RiceServer.Game.Broadcast(ack); 59 | break; 60 | 61 | case "party": 62 | RiceServer.Game.Broadcast(ack); // TODO: broadcast only to users in same party 63 | break; 64 | 65 | case "team": 66 | RiceServer.Game.Broadcast(ack); // TODO: broadcast only to users in same crew 67 | break; 68 | 69 | default: 70 | Log.WriteError("Undefined chat message type."); 71 | break; 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Dealership.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Rice.Game; 7 | using Rice.Server.Structures; 8 | using Rice.Server.Structures.Resources; 9 | 10 | namespace Rice.Server.Packets.Game 11 | { 12 | public static class Dealership 13 | { 14 | [RicePacket(85, RiceServer.ServerType.Game)] 15 | public static void BuyCar(RicePacket packet) 16 | { 17 | Log.WriteDebug($"buycar {BitConverter.ToString(packet.Buffer)}"); 18 | string charName = packet.Reader.ReadUnicodeStatic(21); // what the fug npludo 19 | uint carId = packet.Reader.ReadUInt32(); 20 | uint color = packet.Reader.ReadUInt32(); 21 | 22 | Log.WriteDebug($"char {charName} buying car {carId} color {color}"); 23 | if (!VehicleTable.Vehicles.ContainsKey((int) carId)) 24 | { 25 | packet.Sender.Error("fuckity fuck off"); 26 | Log.WriteError("Attempted to purchase vehicle not in table"); 27 | return; 28 | } 29 | 30 | var vehEntry = VehicleTable.Vehicles[(int) carId]; 31 | if (vehEntry.HasCondition()) 32 | { 33 | Log.WriteError("Attempted to purchase car that has not-null sell condition, not implemented"); 34 | return; 35 | } 36 | 37 | int grade = vehEntry.Level; 38 | var vehUpgradeEntry = VehicleTable.VehicleUpgrades[(int) carId][grade - 1]; 39 | int price = vehUpgradeEntry.Price; 40 | 41 | var character = packet.Sender.Player.ActiveCharacter; 42 | 43 | bool tookMoney = character.SpendMito((ulong) price); 44 | if (!tookMoney) 45 | { 46 | Log.WriteError($"Failed to spend mito for vehicle {carId}"); 47 | return; 48 | } 49 | 50 | Vehicle givenVehicle; 51 | Item givenKey; 52 | bool gaveVehicle = character.GrantVehicle((int) carId, out givenVehicle, out givenKey, (int) color, grade); 53 | if (!gaveVehicle) 54 | { 55 | Log.WriteError($"Failed to grant vehicle {carId}"); 56 | return; 57 | } 58 | 59 | bool selectedCar = character.SelectCar(givenVehicle.CarID); 60 | if (!selectedCar) 61 | { 62 | Log.WriteError($"Failed to select vehicle {carId}"); 63 | return; 64 | } 65 | 66 | var ack = new RicePacket(86); 67 | ack.Writer.Write(givenVehicle.GetInfo()); 68 | ack.Writer.Write(price); 69 | packet.Sender.Send(ack); 70 | 71 | character.FlushModInfo(packet.Sender); 72 | } 73 | 74 | [RicePacket(87, RiceServer.ServerType.Game)] 75 | public static void SellCar(RicePacket packet) 76 | { 77 | string charName = packet.Reader.ReadUnicodeStatic(21); // what the fug npludo 78 | uint carId = packet.Reader.ReadUInt32(); 79 | 80 | var character = packet.Sender.Player.ActiveCharacter; 81 | if (carId == character.CurrentCarID) 82 | { 83 | packet.Sender.Error("dude you're driving that"); 84 | return; 85 | } 86 | 87 | var vehicle = character.Garage.FirstOrDefault(v => v.CarID == carId); 88 | if (vehicle == null) 89 | { 90 | packet.Sender.Error("fuck you man this aint your car"); 91 | Log.WriteError($"Client attempted to select invalid vehicle id {carId}"); 92 | return; 93 | } 94 | 95 | var price = vehicle.VehicleUpgradeEntry.SellPrice; 96 | 97 | bool tookVehicle = character.RemoveVehicle((int) carId); 98 | if (!tookVehicle) 99 | { 100 | Log.WriteError($"Failed to take vehicle {carId} for SellCar"); 101 | return; 102 | } 103 | 104 | bool gaveMoney = character.GrantMito((ulong) price); 105 | if (!gaveMoney) 106 | { 107 | packet.Sender.Error("took your car but you aint gettin any mito sry"); 108 | Log.WriteError($"Failed to give mito for vehicle {carId} for SellCar"); 109 | return; 110 | } 111 | 112 | var ack = new RicePacket(88); 113 | ack.Writer.Write(carId); 114 | ack.Writer.Write((int)price); 115 | packet.Sender.Send(ack); 116 | 117 | character.FlushModInfo(packet.Sender); 118 | } 119 | 120 | [RicePacket(89, RiceServer.ServerType.Game)] 121 | public static void SelectCar(RicePacket packet) 122 | { 123 | uint carId = packet.Reader.ReadUInt32(); 124 | 125 | var character = packet.Sender.Player.ActiveCharacter; 126 | 127 | var vehicle = character.Garage.FirstOrDefault(v => v.CarID == carId); 128 | if (vehicle == null) 129 | { 130 | packet.Sender.Error("fuck you man this aint your car"); 131 | Log.WriteError($"Client attempted to select invalid vehicle id {carId}"); 132 | return; 133 | } 134 | 135 | character.SelectCar((int) carId); 136 | 137 | var ack = new RicePacket(90); 138 | ack.Writer.Write(vehicle.GetInfo()); 139 | packet.Sender.Send(ack); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Inventory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | using Rice.Game; 8 | 9 | namespace Rice.Server.Packets.Game 10 | { 11 | public static class Inventory 12 | { 13 | [RicePacket(400, RiceServer.ServerType.Game, CheckedIn = true)] 14 | public static void ItemList(RicePacket packet) 15 | { 16 | // unsigned long CurCarID (which car the item's currently equipped on, if any) (uncertain about CurCarID/LastCarID, check with Coaster later) 17 | // unsigned short State 18 | // unsigned short Slot 19 | // long StackNum 20 | // unsigned long LastCarID (which car the item was last equipped on, or the car ID associated with the car if the item's a key) 21 | // unsigned long AssistA 22 | // unsigned long AssistB 23 | // unsigned long AssistC 24 | // unsigned long AssistD 25 | // unsigned long AssistE 26 | // unsigned long AssistF 27 | // unsigned long AssistG 28 | // unsigned long AssistH 29 | // unsigned long AssistI 30 | // unsigned long AssistJ 31 | // unsigned long Box 32 | // unsigned long Belonging 33 | // long Upgrade 34 | // long UpgradePoint 35 | // unsigned long ExpireTick 36 | // float ItemHealth (only applicable to NEO parts, -1 everywhere else) 37 | // unsigned long unk2 (?, always 0) 38 | // unsigned long TableIdx 39 | // unsigned long InvenIdx 40 | // long Random (random seed, only applies to certain item types) 41 | var inventory = packet.Sender.Player.ActiveCharacter.Inventory; 42 | var ack = new RicePacket(401); 43 | ack.Writer.Write(262144); // ListUpdate (262144 = First packet from list queue, 262145 = sequential) 44 | ack.Writer.Write(inventory.Count); // ItemNum 45 | foreach (var item in inventory) 46 | { 47 | ack.Writer.Write(item.GetInfo()); 48 | } 49 | packet.Sender.Send(ack); 50 | } 51 | 52 | [RicePacket(403, RiceServer.ServerType.Game, CheckedIn = true)] 53 | public static void DropItem(RicePacket packet) 54 | { 55 | var tableIdx = packet.Reader.ReadInt32(); 56 | var invIdx = packet.Reader.ReadInt32(); 57 | var count = packet.Reader.ReadInt32(); 58 | 59 | var character = packet.Sender.Player.ActiveCharacter; 60 | 61 | var toDrop = character.Inventory.FirstOrDefault(i => i.invIdx == invIdx && i.tblIdx == tableIdx); 62 | if (toDrop == null || count < 1) 63 | { 64 | packet.Sender.Error("What item?"); 65 | Log.WriteError("What item?"); 66 | return; 67 | } 68 | 69 | Log.WriteDebug($"{tableIdx} {invIdx} {count} item {toDrop?.ID ?? "NONE"}"); 70 | bool dropped = character.DropItem((int) toDrop.invIdx, out toDrop, (uint) count); 71 | if (!dropped) 72 | { 73 | packet.Sender.Error("Failed to drop item."); 74 | Log.WriteError("Failed to drop item."); 75 | return; 76 | } 77 | 78 | //DropItemAck 79 | var ack = new RicePacket(404); 80 | ack.Writer.Write(tableIdx); 81 | ack.Writer.Write(invIdx); 82 | ack.Writer.Write(count); 83 | packet.Sender.Send(ack); 84 | 85 | Log.WriteDebug($"todrop stack {toDrop.stackNum}"); 86 | 87 | character.FlushModInfo(packet.Sender); 88 | } 89 | 90 | [RicePacket(409, RiceServer.ServerType.Game, CheckedIn = true)] 91 | public static void EquipItem(RicePacket packet) 92 | { 93 | var invIdx = packet.Reader.ReadUInt32(); 94 | var destSlotIdx = packet.Reader.ReadUInt32(); 95 | var carId = packet.Reader.ReadUInt32(); 96 | 97 | var character = packet.Sender.Player.ActiveCharacter; 98 | 99 | Log.WriteDebug($"equip item {invIdx} slot {destSlotIdx} car {carId}"); 100 | 101 | bool equipped = character.EquipItem((int)invIdx, (short)destSlotIdx, (int)carId); 102 | if (!equipped) 103 | { 104 | packet.Sender.Error("could not equip item"); 105 | Log.WriteError($"Could not equip item {invIdx}"); 106 | return; 107 | } 108 | 109 | var ack = new RicePacket(410); 110 | ack.Writer.Write(invIdx); 111 | ack.Writer.Write(destSlotIdx); 112 | ack.Writer.Write(carId); 113 | packet.Sender.Send(ack); 114 | 115 | character.FlushModInfo(packet.Sender); 116 | } 117 | 118 | [RicePacket(411, RiceServer.ServerType.Game, CheckedIn = true)] 119 | public static void UnEquipItem(RicePacket packet) 120 | { 121 | var invIdx = packet.Reader.ReadUInt32(); 122 | var carId = packet.Reader.ReadUInt32(); 123 | 124 | var character = packet.Sender.Player.ActiveCharacter; 125 | 126 | Log.WriteDebug($"unequip item {invIdx} car {carId}"); 127 | 128 | bool unEquipped = character.UnEquipItem((int)invIdx, (int)carId); 129 | if (!unEquipped) 130 | { 131 | packet.Sender.Error("could not unequip item"); 132 | Log.WriteError($"Could not unequip item {invIdx}"); 133 | return; 134 | } 135 | 136 | var ack = new RicePacket(412); 137 | ack.Writer.Write(invIdx); 138 | ack.Writer.Write(carId); 139 | packet.Sender.Send(ack); 140 | 141 | character.FlushModInfo(packet.Sender); 142 | } 143 | } 144 | } -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Server.Packets.Game 9 | { 10 | public static class Misc 11 | { 12 | [RicePacket(784, RiceServer.ServerType.Game)] 13 | public static void GetDateTime(RicePacket packet) 14 | { 15 | /* 16 | var ack = new RicePacket(785); 17 | var timestamp = (Int32)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; 18 | ack.Writer.Write(packet.Reader.ReadUInt32()); 19 | ack.Writer.Write(packet.Reader.ReadUInt32()); 20 | ack.Writer.Write(packet.Reader.ReadSingle()); 21 | ack.Writer.Write(Environment.TickCount); 22 | ack.Writer.Write(timestamp); 23 | ack.Writer.Write(1); 24 | ack.Writer.Write((short)DateTime.Now.Year); 25 | ack.Writer.Write((short)DateTime.Now.Month); 26 | ack.Writer.Write((short)DateTime.Now.Day); 27 | ack.Writer.Write((short)DateTime.Now.DayOfWeek); 28 | ack.Writer.Write((byte)DateTime.Now.Hour); 29 | ack.Writer.Write((byte)DateTime.Now.Minute); 30 | ack.Writer.Write((byte)DateTime.Now.Second); 31 | ack.Writer.Write((byte)0); 32 | packet.Sender.Send(ack); 33 | */ 34 | } 35 | [RicePacket(120, RiceServer.ServerType.Game)] 36 | public static void CheckInGame(RicePacket packet) 37 | { 38 | Log.WriteLine("CheckInGame"); 39 | var version = packet.Reader.ReadUInt32(); 40 | var ticket = packet.Reader.ReadUInt32(); 41 | var username = packet.Reader.ReadUnicodeStatic(32); 42 | // Followed by int m_IsPcBang, 21 bytes of weird shit we don't know about, and yet another 21 byte chunk of weird shit we don't know about. 43 | 44 | var serverTicket = RiceServer.GetTicket(ticket); 45 | 46 | if (serverTicket == null || !serverTicket.ValidateOrigin(packet.Sender, username)) 47 | { 48 | Log.WriteLine("Ticket is non-existent or invalid for current user."); 49 | Log.WriteLine("ticket: {0}, packet sender: {1}, username: {2}", serverTicket.Identifier, packet.Sender.GetRemoteIP(), username); 50 | packet.Sender.Error("water u even doin"); 51 | return; 52 | } 53 | 54 | packet.Sender.Player = serverTicket.GetOwner(); 55 | packet.Sender.Player.GameClient = packet.Sender; 56 | 57 | var ack = new RicePacket(121); 58 | ack.Writer.Write(1L); 59 | packet.Sender.Send(ack); 60 | } 61 | 62 | [RicePacket(125, RiceServer.ServerType.Game)] 63 | public static void JoinChannel(RicePacket packet) 64 | { 65 | var serial = RiceServer.CreateSerial(packet.Sender); 66 | 67 | var ack = new RicePacket(126); 68 | ack.Writer.WriteUnicodeStatic("speeding", 10); 69 | ack.Writer.WriteUnicodeStatic(packet.Sender.Player.ActiveCharacter.Name, 16); 70 | ack.Writer.Write(serial.Identifier); 71 | ack.Writer.Write((ushort)123); // Age 72 | packet.Sender.Send(ack); 73 | } 74 | 75 | [RicePacket(4, RiceServer.ServerType.Game)] 76 | public static void Latency(RicePacket packet) 77 | { 78 | // TODO: log SkidRush and check this 79 | 80 | float time = packet.Reader.ReadSingle(); 81 | //Log.WriteLine("Latency: {0}", time); 82 | 83 | var ack = new RicePacket(4); // no idea if this is right, pls check Cmd_Latency on client 84 | ack.Writer.Write(time); 85 | packet.Sender.Send(ack); 86 | } 87 | 88 | [RicePacket(3916, RiceServer.ServerType.Game)] 89 | public static void LatencyRelated(RicePacket packet) 90 | { 91 | // TODO: log SkidRush and check this 92 | // possibly Cmd_PartyPing (client's Cmd_PartyPing handler is 3915) 93 | 94 | float time = packet.Reader.ReadSingle(); 95 | //Log.WriteLine("Latency??: {0}", BitConverter.ToString(packet.Buffer)); 96 | } 97 | 98 | [RicePacket(3917, RiceServer.ServerType.Game)] 99 | public static void UnknownSync(RicePacket packet) 100 | { 101 | // hide sync packets for now 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/PartShop.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Rice.Game; 7 | using Rice.Server.Structures.Resources; 8 | 9 | namespace Rice.Server.Packets.Game 10 | { 11 | public static class PartShop 12 | { 13 | [RicePacket(405, RiceServer.ServerType.Game)] 14 | public static void BuyItem(RicePacket packet) 15 | { 16 | int itemID = packet.Reader.ReadInt32(); 17 | uint count = packet.Reader.ReadUInt32(); 18 | uint shopID = packet.Reader.ReadUInt32(); // possibly (possibly fucking not theres no way that's 4 billion) 19 | 20 | Log.WriteDebug($"id {itemID} count {count} shop {shopID}"); 21 | 22 | if (itemID > ItemTable.Items.Count) 23 | { 24 | packet.Sender.Error("that id can't be right"); 25 | return; 26 | } 27 | 28 | var item = ItemTable.Items[itemID]; 29 | int itemBuyPrice; 30 | bool gotPrice = int.TryParse(item.BuyValue, out itemBuyPrice); 31 | if (item.BuyValue == "n/a" || !gotPrice) 32 | { 33 | packet.Sender.Error("price pls"); 34 | return; 35 | } 36 | 37 | Log.WriteDebug($"item {item.Name} {itemBuyPrice}"); 38 | 39 | var character = packet.Sender.Player.ActiveCharacter; 40 | 41 | bool tookMoney = character.SpendMito((ulong) itemBuyPrice); 42 | if (!tookMoney) 43 | return; 44 | 45 | Item resultItem; 46 | 47 | bool gaveItem = character.GrantItem(item.ID, count, out resultItem); 48 | if (!gaveItem) 49 | return; 50 | 51 | var ack = new RicePacket(406); 52 | ack.Writer.Write(itemID); 53 | ack.Writer.Write(count); 54 | ack.Writer.Write(itemBuyPrice * count); 55 | packet.Sender.Send(ack); 56 | 57 | character.FlushModInfo(packet.Sender); 58 | } 59 | 60 | [RicePacket(407, RiceServer.ServerType.Game)] 61 | public static void SellItem(RicePacket packet) 62 | { 63 | uint itemID = packet.Reader.ReadUInt32(); 64 | uint count = packet.Reader.ReadUInt32(); 65 | uint invenID = packet.Reader.ReadUInt32(); 66 | 67 | if (itemID > ItemTable.Items.Count) 68 | { 69 | packet.Sender.Error("that id can't be right"); 70 | return; 71 | } 72 | 73 | var item = ItemTable.Items[(int)itemID]; 74 | int itemSellPrice; 75 | bool gotPrice = int.TryParse(item.SellValue, out itemSellPrice); 76 | if (item.SellValue == "n/a" || !gotPrice) 77 | { 78 | packet.Sender.Error("price pls"); 79 | return; 80 | } 81 | 82 | var character = packet.Sender.Player.ActiveCharacter; 83 | 84 | Item toSell; 85 | bool tookItem = character.DropItem((int)invenID, out toSell, count); 86 | if (!tookItem) 87 | return; 88 | 89 | long mitoToGive = itemSellPrice * count; 90 | bool gaveMoney = character.GrantMito((ulong)mitoToGive); 91 | if (!gaveMoney) 92 | { 93 | packet.Sender.Error("Well that's embarrassing. Took your item but no mito for you, sorry"); 94 | return; 95 | } 96 | 97 | var ack = new RicePacket(408); 98 | ack.Writer.Write(itemID); 99 | ack.Writer.Write(count); 100 | ack.Writer.Write((uint)mitoToGive); 101 | ack.Writer.Write(invenID); 102 | packet.Sender.Send(ack); 103 | 104 | character.FlushModInfo(packet.Sender); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Party.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Rice.Server.Packets.Game 8 | { 9 | public class Party 10 | { 11 | [RicePacket(228, RiceServer.ServerType.Game, CheckedIn = true)] 12 | public static void PartyPreCheck(RicePacket packet) 13 | { 14 | int serial = packet.Reader.ReadInt32(); 15 | string invitee = packet.Reader.ReadUnicodeStatic(21); 16 | Log.WriteLine("PartyPreCheck: {0}", BitConverter.ToString(packet.Buffer)); 17 | 18 | var ack = new RicePacket(229); 19 | ack.Writer.Write(0); // Serial, generate and store for invite? 20 | ack.Writer.Write(0L); // Cid 21 | ack.Writer.WriteUnicodeStatic(invitee, 21); // char name 22 | ack.Writer.Write(-1); // PartyId (should be -1 if not in party) 23 | ack.Writer.WriteUnicodeStatic("", 30); // PartyName 24 | packet.Sender.Send(ack); 25 | } 26 | 27 | [RicePacket(240, RiceServer.ServerType.Game, CheckedIn = true)] 28 | public static void PartyInvite(RicePacket packet) 29 | { 30 | uint serial = packet.Reader.ReadUInt32(); 31 | uint ticket = packet.Reader.ReadUInt32(); 32 | 33 | //xistrpartymemberinfo 34 | long charCid = packet.Reader.ReadInt64(); 35 | string charName = packet.Reader.ReadUnicodeStatic(21); 36 | ushort charAvatar = packet.Reader.ReadUInt16(); 37 | ushort charLevel = packet.Reader.ReadUInt16(); 38 | 39 | string partyName = packet.Reader.ReadUnicodeStatic(30); 40 | string inviteMsg = packet.Reader.ReadUnicodeStatic(100); 41 | //server seems to create a party and relay this packet 42 | Log.WriteLine("PartyInvite: {0}", BitConverter.ToString(packet.Buffer)); 43 | } 44 | 45 | [RicePacket(241, RiceServer.ServerType.Game, CheckedIn = true)] 46 | public static void PartyReject(RicePacket packet) 47 | { 48 | Log.WriteLine("PartyReject: {0}", BitConverter.ToString(packet.Buffer)); 49 | } 50 | 51 | [RicePacket(242, RiceServer.ServerType.Game, CheckedIn = true)] 52 | public static void PartyJoin(RicePacket packet) 53 | { 54 | Log.WriteLine("PartyJoin: {0}", BitConverter.ToString(packet.Buffer)); 55 | } 56 | 57 | [RicePacket(244, RiceServer.ServerType.Game, CheckedIn = true)] 58 | public static void PartyLeave(RicePacket packet) 59 | { 60 | Log.WriteLine("PartyLeave: {0}", BitConverter.ToString(packet.Buffer)); 61 | } 62 | 63 | [RicePacket(245, RiceServer.ServerType.Game, CheckedIn = true)] 64 | public static void PartyBanish(RicePacket packet) 65 | { 66 | Log.WriteLine("PartyBanish: {0}", BitConverter.ToString(packet.Buffer)); 67 | } 68 | 69 | [RicePacket(246, RiceServer.ServerType.Game, CheckedIn = true)] 70 | public static void PartyApply(RicePacket packet) 71 | { 72 | Log.WriteLine("PartyApply: {0}", BitConverter.ToString(packet.Buffer)); 73 | } 74 | 75 | [RicePacket(247, RiceServer.ServerType.Game, CheckedIn = true)] 76 | public static void PartyAccept(RicePacket packet) 77 | { 78 | Log.WriteLine("PartyAccept: {0}", BitConverter.ToString(packet.Buffer)); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/Quest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Rice.Server.Core; 6 | using Rice.Server.Structures; 7 | 8 | namespace Rice.Server.Packets.Game 9 | { 10 | public static class Quest 11 | { 12 | [RicePacket(272, RiceServer.ServerType.Game, CheckedIn = true)] 13 | public static void MyQuestList(RicePacket packet) 14 | { 15 | var quests = Rice.Game.Quest.Retrieve(packet.Sender.Player.ActiveCharacter.CID); 16 | 17 | var ack = new RicePacket(273); 18 | ack.Writer.Write(quests.Count); 19 | foreach (var quest in quests) 20 | { 21 | ack.Writer.Write(quest.QID); 22 | ack.Writer.Write(quest.State); 23 | ack.Writer.Write(quest.Progress); 24 | ack.Writer.Write(quest.FailCount); 25 | } 26 | packet.Sender.Send(ack); 27 | } 28 | 29 | [RicePacket(262, RiceServer.ServerType.Game, CheckedIn = true)] 30 | public static void QuestStart(RicePacket packet) 31 | { 32 | var questId = packet.Reader.ReadInt32(); 33 | 34 | var quest = Rice.Game.Quest.Retrieve(packet.Sender.Player.ActiveCharacter.CID, questId); 35 | quest = quest ?? Rice.Game.Quest.CreateEntry(packet.Sender.Player.ActiveCharacter.CID, questId); 36 | 37 | packet.Sender.Player.ActiveCharacter.ActiveQuest = quest; 38 | 39 | var ack = new RicePacket(263); 40 | ack.Writer.Write(quest.QID); 41 | ack.Writer.Write((uint) quest.FailCount); 42 | packet.Sender.Send(ack); 43 | // TODO: verify if player is eligible 44 | } 45 | 46 | [RicePacket(264, RiceServer.ServerType.Game, CheckedIn = true)] 47 | public static void QuestComplete(RicePacket packet) 48 | { 49 | var questId = packet.Reader.ReadInt32(); 50 | var totalTime = packet.Reader.ReadSingle(); 51 | 52 | var quest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 53 | if (quest == null || quest.QID != questId) 54 | { 55 | packet.Sender.Error("something's fucky"); 56 | packet.Sender.Kill("Active quest and quest complete mismatch."); 57 | return; 58 | } 59 | 60 | quest.SetState(1); 61 | 62 | var ack = new RicePacket(265); 63 | ack.Writer.Write(questId); 64 | packet.Sender.Send(ack); 65 | // TODO: verify quest completion 66 | } 67 | 68 | [RicePacket(266, RiceServer.ServerType.Game, CheckedIn = true)] 69 | public static void QuestReward(RicePacket packet) 70 | { 71 | var questId = packet.Reader.ReadInt32(); 72 | 73 | var quest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 74 | if (quest == null || quest.QID != questId) 75 | { 76 | packet.Sender.Error("something's fucky"); 77 | packet.Sender.Kill("Active quest and quest reward mismatch."); 78 | return; 79 | } 80 | 81 | quest.SetState(2); 82 | var rewards = quest.QuestInfo.GetRewards(); 83 | packet.Sender.Player.ActiveCharacter.ActiveQuest = null; 84 | 85 | int expGotten = quest.QuestInfo.Experience; 86 | int moneyGotten = quest.QuestInfo.Mito; 87 | 88 | packet.Sender.Player.ActiveCharacter.GrantExperience(expGotten); 89 | packet.Sender.Player.ActiveCharacter.GrantMito((ulong)moneyGotten); 90 | 91 | var ack = new RicePacket(267); 92 | ack.Writer.Write(questId); 93 | ack.Writer.Write(expGotten); 94 | ack.Writer.Write(moneyGotten); 95 | ack.Writer.Write(packet.Sender.Player.ActiveCharacter.GetExpInfo()); 96 | ack.Writer.Write(packet.Sender.Player.ActiveCharacter.Level); 97 | ack.Writer.Write((ushort) rewards.Length); // reward item num 98 | Console.WriteLine("{0}", ack.Writer.BaseStream.Position); 99 | for (int i = 0; i < 3; i++) 100 | { 101 | // jesus christ i need to clean up this horrible hack 102 | for (; i < rewards.Length; i++) 103 | { 104 | var itemID = rewards[i]; 105 | int tblIdx = Structures.Resources.ItemTable.Items.FindIndex(itm => itm.ID == itemID); 106 | if (tblIdx == -1) 107 | { 108 | ack.Writer.Write(0); 109 | continue; 110 | } 111 | Rice.Game.Item resultItem; 112 | packet.Sender.Player.ActiveCharacter.GrantItem(itemID, 1, out resultItem); 113 | ack.Writer.Write((uint) tblIdx); 114 | } 115 | if (i < 3) 116 | ack.Writer.Write(0); 117 | } 118 | packet.Sender.Send(ack); 119 | // TODO: use real quest data, update player info 120 | } 121 | 122 | [RicePacket(268, RiceServer.ServerType.Game, CheckedIn = true)] 123 | public static void QuestFail(RicePacket packet) 124 | { 125 | var questId = packet.Reader.ReadInt32(); 126 | 127 | var quest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 128 | if (quest == null || quest.QID != questId) 129 | { 130 | packet.Sender.Error("something's fucky"); 131 | packet.Sender.Kill("Active quest and quest fail mismatch."); 132 | return; 133 | } 134 | 135 | quest.Fail(); 136 | packet.Sender.Player.ActiveCharacter.ActiveQuest = null; 137 | 138 | var ack = new RicePacket(269); 139 | ack.Writer.Write(questId); 140 | packet.Sender.Send(ack); 141 | } 142 | 143 | [RicePacket(270, RiceServer.ServerType.Game, CheckedIn = true)] 144 | public static void QuestGiveUp(RicePacket packet) 145 | { 146 | var questId = packet.Reader.ReadInt32(); 147 | 148 | var quest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 149 | if (quest == null || quest.QID != questId) 150 | { 151 | packet.Sender.Error("something's fucky"); 152 | packet.Sender.Kill("Active quest and quest abandon mismatch."); 153 | return; 154 | } 155 | 156 | quest.Fail(true); 157 | packet.Sender.Player.ActiveCharacter.ActiveQuest = null; 158 | 159 | var ack = new RicePacket(271); 160 | ack.Writer.Write(questId); 161 | packet.Sender.Send(ack); 162 | } 163 | 164 | [RicePacket(274, RiceServer.ServerType.Game, CheckedIn = true)] 165 | public static void QuestGoalPlace(RicePacket packet) 166 | { 167 | var questId = packet.Reader.ReadInt32(); 168 | 169 | var quest = packet.Sender.Player.ActiveCharacter.ActiveQuest; 170 | if (quest == null || quest.QID != questId) 171 | { 172 | packet.Sender.Error("something's fucky"); 173 | packet.Sender.Kill("Active quest and quest progress mismatch."); 174 | return; 175 | } 176 | 177 | quest.IncrementProgress(); 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /Rice/Server/Packets/Game/VisualShop.cs: -------------------------------------------------------------------------------- 1 | using Rice.Server.Core; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using Rice.Game; 7 | using Rice.Server.Structures.Resources; 8 | 9 | namespace Rice.Server.Packets.Game 10 | { 11 | public static class VisualShop 12 | { 13 | [RicePacket(1400, RiceServer.ServerType.Game)] 14 | public static void GetMyHancoin(RicePacket packet) 15 | { 16 | var ack = new RicePacket(1401); 17 | ack.Writer.Write((uint)packet.Sender.Player.User.Credits); 18 | ack.Writer.Write(0L); // mileage?? masangpluto pls 19 | packet.Sender.Send(ack); 20 | } 21 | 22 | [RicePacket(1203, RiceServer.ServerType.Game)] 23 | public static void BuyVisualItem(RicePacket packet) 24 | { 25 | uint tableIdx = packet.Reader.ReadUInt32(); 26 | uint carId = packet.Reader.ReadUInt32(); 27 | string plateName = packet.Reader.ReadUnicodeStatic(20); 28 | uint periodIdx = packet.Reader.ReadUInt32(); 29 | bool useMileage = packet.Reader.ReadUInt16() > 0; 30 | long curCash = packet.Reader.ReadInt64(); // don't use this, obviously 31 | Log.WriteDebug($"BuyVisualItem idx {tableIdx} car {carId} plate {plateName} period {periodIdx} mileage? {useMileage} cash {curCash}"); 32 | 33 | // TODO: complete stub 34 | } 35 | 36 | [RicePacket(1300, RiceServer.ServerType.Game)] 37 | public static void BuyHistoryList(RicePacket packet) 38 | { 39 | uint offset = packet.Reader.ReadUInt32(); 40 | uint count = packet.Reader.ReadUInt32(); 41 | uint tab = packet.Reader.ReadUInt32(); // 1 = purchase history, 2 = sent gift, 3 = received gift 42 | Log.WriteDebug($"BuyHistoryList offset {offset} count {count} tab {tab}"); 43 | 44 | // TODO: complete stub 45 | } 46 | 47 | [RicePacket(1306, RiceServer.ServerType.Game)] 48 | public static void IsValidCharName(RicePacket packet) 49 | { 50 | string charName = packet.Reader.ReadUnicodeStatic(21); 51 | Log.WriteDebug($"IsValidCharName name {charName}"); 52 | 53 | // TODO: complete stub 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Lobby/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Game; 7 | using Rice.Server.Core; 8 | 9 | namespace Rice.Server.Packets.Lobby 10 | { 11 | public static class Misc 12 | { 13 | [RicePacket(41, RiceServer.ServerType.Lobby)] 14 | public static void CheckInLobby(RicePacket packet) 15 | { 16 | Log.WriteLine("CheckInLobby request."); 17 | 18 | uint version = packet.Reader.ReadUInt32(); 19 | uint ticket = packet.Reader.ReadUInt32(); 20 | string username = packet.Reader.ReadUnicodeStatic(0x28); 21 | uint time = packet.Reader.ReadUInt32(); 22 | string stringTicket = packet.Reader.ReadASCIIStatic(0x40); 23 | 24 | var serverTicket = RiceServer.GetTicket(ticket); 25 | 26 | if (serverTicket == null || !serverTicket.ValidateOrigin(packet.Sender, username)) 27 | { 28 | #if DEBUG 29 | packet.Sender.Player = new Player(Rice.Game.User.Retrieve(username)); 30 | RiceServer.AddPlayer(packet.Sender.Player); 31 | serverTicket = RiceServer.CreateDebugTicket(packet.Sender, ticket); 32 | #else 33 | Log.WriteLine("Ticket is non-existent or invalid for current user."); 34 | packet.Sender.Error("water u even doin"); 35 | return; 36 | #endif 37 | } 38 | else 39 | { 40 | packet.Sender.Player = serverTicket.GetOwner(); 41 | } 42 | 43 | 44 | var ack = new RicePacket(42); // CheckInLobbyAck 45 | ack.Writer.Write(ticket); // Ticket 46 | ack.Writer.Write(0); // Permission ??? 47 | packet.Sender.Send(ack); 48 | 49 | var timeAck = new RicePacket(47); // LobbyTimeAck 50 | timeAck.Writer.Write(Environment.TickCount); 51 | timeAck.Writer.Write(Environment.TickCount); 52 | packet.Sender.Send(timeAck); 53 | 54 | Log.WriteLine("User {0} entered lobby.", username); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Lobby/User.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Rice.Server.Core; 8 | 9 | namespace Rice.Server.Packets.Lobby 10 | { 11 | public static class User 12 | { 13 | [RicePacket(60, RiceServer.ServerType.Lobby)] 14 | public static void UserInfo(RicePacket packet) 15 | { 16 | Log.WriteLine("UserInfo request."); 17 | 18 | var settings = new RicePacket(30); 19 | settings.Writer.Write(Static.GameSettings); 20 | packet.Sender.Send(settings); 21 | 22 | packet.Sender.Player.Characters = Rice.Game.Character.Retrieve(packet.Sender.Player.User.UID); 23 | 24 | var ack = new RicePacket(61); 25 | 26 | ack.Writer.Write(0); // Permissions 27 | ack.Writer.Write(packet.Sender.Player.Characters.Count); // Character Count 28 | 29 | ack.Writer.WriteUnicodeStatic(packet.Sender.Player.User.Name, 18); // Username 30 | ack.Writer.Write((long)0); 31 | ack.Writer.Write((long)0); 32 | ack.Writer.Write((long)0); 33 | ack.Writer.Write(0); 34 | 35 | foreach (var character in packet.Sender.Player.Characters) 36 | { 37 | ack.Writer.WriteUnicodeStatic(character.Name, 21); // Name 38 | ack.Writer.Write(character.CID); // ID 39 | ack.Writer.Write(character.Avatar); // Avatar 40 | ack.Writer.Write(character.Level); // Level 41 | ack.Writer.Write(1); // Car ID 42 | ack.Writer.Write(0x50); // Car type 43 | ack.Writer.Write(0); // Car color 44 | ack.Writer.Write(DateTime.Now - TimeSpan.FromDays(365)); // Creation Date 45 | ack.Writer.Write(character.TID); // Crew ID 46 | ack.Writer.Write(0L); // Crew Image 47 | ack.Writer.WriteUnicodeStatic("", 13); // Crew Name 48 | ack.Writer.Write((short)0); // Crew rank 49 | ack.Writer.Write((short)0); // Guild (0 = OMD, 1 = ROO) 50 | } 51 | 52 | packet.Sender.Send(ack); 53 | 54 | Log.WriteLine("Sent character list."); 55 | } 56 | 57 | [RicePacket(80, RiceServer.ServerType.Lobby)] 58 | public static void CheckCharacterName(RicePacket packet) 59 | { 60 | string characterName = packet.Reader.ReadUnicode(); 61 | 62 | var ack = new RicePacket(81); 63 | ack.Writer.WriteUnicodeStatic(characterName, 21); 64 | ack.Writer.Write(Rice.Game.Character.IsNameUsable(characterName) ? 1 : 0); // Availability. 1 = Available, 0 = Unavailable. 65 | packet.Sender.Send(ack); 66 | } 67 | 68 | [RicePacket(82, RiceServer.ServerType.Lobby)] 69 | public static void CreateCharacter(RicePacket packet) 70 | { 71 | string characterName = packet.Reader.ReadUnicodeStatic(21); 72 | ushort avatar = packet.Reader.ReadUInt16(); 73 | // We disregard the next 8 bytes (ulong CarType, ulong Color) 74 | 75 | if(packet.Sender.Player.Characters.Count() < 3 && Rice.Game.Character.IsNameUsable(characterName)) 76 | { 77 | if (Rice.Game.Character.Create(packet.Sender.Player.User.UID, characterName, avatar)) 78 | { 79 | Rice.Game.Character character = Rice.Game.Character.Retrieve(characterName); 80 | if (character != null) 81 | { 82 | packet.Sender.Player.Characters.Add(character); 83 | var ack = new RicePacket(83); 84 | ack.Writer.WriteUnicodeStatic(characterName, 21); 85 | ack.Writer.Write(character.CID); //High hopes there, NPLUTO 86 | ack.Writer.Write(character.CurrentCarID); //??? 87 | packet.Sender.Send(ack); 88 | } 89 | } 90 | } 91 | // TODO: Verify, Handle 92 | } 93 | 94 | [RicePacket(50, RiceServer.ServerType.Lobby)] 95 | public static void DeleteCharacter(RicePacket packet) 96 | { 97 | string characterName = packet.Reader.ReadUnicode(); 98 | foreach(var character in packet.Sender.Player.Characters) 99 | { 100 | if (character.Name == characterName) 101 | { 102 | if (Rice.Game.Character.Delete(characterName)) 103 | { 104 | packet.Sender.Player.Characters.Remove(character); 105 | var ack = new RicePacket(84); 106 | ack.Writer.WriteUnicodeStatic(characterName, 21); 107 | packet.Sender.Send(ack); 108 | break; 109 | } 110 | break; 111 | } 112 | } 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Rice/Server/Packets/Static.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Rice.Server.Packets 8 | { 9 | // TODO: Keep this as empty as possible 10 | public static class Static 11 | { 12 | public static byte[] GameSettings = new byte[] { 13 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 14 | 0x06, 0x00, 0x2F, 0x37, 0x05, 0x00, 0x00, 0x00, 0x0F, 0x00, 0xE4, 0xAF, 0x77, 0x00, 0xE8, 0x0F, 15 | 0x8B, 0x14, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x28, 16 | 0x20, 0x1C, 0x08, 0x87, 0x01, 0x01, 0x58, 0x02, 0x00, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0xFF, 0x7B, 0x27, 0xE3, 0xCD, 0xF0, 0x8C, 0x02, 0x0A, 0x5A, 0xAA, 0xD5, 0x29, 0x00, 18 | 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x05, 0xE0, 0x51, 0x00, 0x41, 0x60, 0xC8, 0x00, 0x00, 0x08, 19 | 0x28, 0x02, 0x88, 0x12, 0x83, 0x00, 0x64, 0x80, 0x5A, 0xCB, 0x01, 0x07, 0x30, 0x08, 0x00, 0x00, 20 | 0x00, 0x00 }; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Rice/Server/Resources.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Rice.Server.Structures.Resources; 6 | 7 | namespace Rice.Server 8 | { 9 | public static class Resources 10 | { 11 | public static void Initialize(Config config) 12 | { 13 | QuestTable.Load("res/QuestClient.json"); 14 | ItemTable.Load("res/ItemClient.json", "res/UseItemClient.json"); 15 | VehicleTable.Load("res/VehicleUpgrade.json", "res/VehicleList.json"); 16 | AssistTable.Load("res/AssistClient.json"); 17 | 18 | Log.WriteLine("Loaded resources."); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Rice/Server/RiceServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Net; 4 | using System.Reflection; 5 | using System.Collections.Generic; 6 | using Rice.Game; 7 | 8 | namespace Rice.Server.Core 9 | { 10 | public static class RiceServer 11 | { 12 | [Flags] 13 | public enum ServerType 14 | { 15 | Auth = 1, 16 | Lobby = 2, 17 | Game = 4, 18 | Area = 8, 19 | Ranking = 16 20 | } 21 | 22 | public static RiceListener Auth, Lobby, Game, Area, Ranking; 23 | public static Config Config; 24 | public static Random RNG; 25 | 26 | static List validSerials = new List(); 27 | static List validTickets = new List(); 28 | 29 | private static List areas; 30 | private static List players; 31 | 32 | public static long[] ExpTable, ExpSumTable; 33 | 34 | static ushort serialCounter = 1; 35 | 36 | public static void Initialize(Config config) 37 | { 38 | Log.WriteLine("Initializing RiceServer."); 39 | 40 | Config = config; 41 | 42 | Auth = new RiceListener("Auth", Config.AuthPort); 43 | Lobby = new RiceListener("Lobby", Config.LobbyPort); 44 | Game = new RiceListener("Game", Config.GamePort); 45 | Area = new RiceListener("Area", Config.AreaPort, false); 46 | Ranking = new RiceListener("Ranking", Config.RankingPort); 47 | 48 | players = new List(); 49 | areas = new List(); 50 | RNG = new Random(); 51 | 52 | ExpTable = new long[150]; 53 | ExpSumTable = new long[ExpTable.Length]; 54 | long sum = 0; 55 | for (int i = 1; i < ExpTable.Length; i++) 56 | { 57 | double missionTime = Math.Pow(i, 2.25) * 1.5; 58 | double realGrind = Math.Pow(i, 20) / Math.Pow(10, 33); 59 | long exp = (long) (100 + missionTime + realGrind); 60 | ExpTable[i] = exp; 61 | sum += exp; 62 | ExpSumTable[i] = sum; 63 | } 64 | 65 | loadParsers(); 66 | } 67 | 68 | private static void loadParsers() 69 | { 70 | foreach (var type in Assembly.GetExecutingAssembly().GetTypes()) 71 | { 72 | foreach (var method in type.GetMethods()) 73 | { 74 | foreach (var boxedAttrib in method.GetCustomAttributes(typeof(RicePacketAttribute), false)) 75 | { 76 | var attrib = boxedAttrib as RicePacketAttribute; 77 | 78 | var id = attrib.ID; 79 | var parser = (Action)Delegate.CreateDelegate(typeof(Action), method); 80 | 81 | if (attrib.Handlers.HasFlag(ServerType.Auth)) 82 | Auth.SetParser(id, parser, attrib.CheckedIn); 83 | 84 | if (attrib.Handlers.HasFlag(ServerType.Lobby)) 85 | Lobby.SetParser(id, parser, attrib.CheckedIn); 86 | 87 | if (attrib.Handlers.HasFlag(ServerType.Game)) 88 | Game.SetParser(id, parser, attrib.CheckedIn); 89 | 90 | if (attrib.Handlers.HasFlag(ServerType.Area)) 91 | Area.SetParser(id, parser, attrib.CheckedIn); 92 | 93 | if (attrib.Handlers.HasFlag(ServerType.Ranking)) 94 | Ranking.SetParser(id, parser, attrib.CheckedIn); 95 | } 96 | } 97 | } 98 | 99 | } 100 | 101 | public static void Start() 102 | { 103 | Log.WriteLine("Starting RiceServer."); 104 | 105 | Auth.Start(); 106 | Lobby.Start(); 107 | Game.Start(); 108 | Area.Start(); 109 | Ranking.Start(); 110 | 111 | Log.WriteLine("RiceServer started."); 112 | } 113 | 114 | 115 | public static Player[] GetPlayers() 116 | { 117 | return players.ToArray(); 118 | } 119 | 120 | public static void AddPlayer(Player player) 121 | { 122 | players.Add(player); 123 | } 124 | 125 | public static void RemovePlayer(Player player) 126 | { 127 | players.Remove(player); 128 | } 129 | 130 | public static Serial CreateSerial(RiceClient client) 131 | { 132 | var serial = new Serial(client) {Identifier = serialCounter++}; 133 | validSerials.Add(serial); 134 | return serial; 135 | } 136 | 137 | public static Ticket CreateTicket(RiceClient client) 138 | { 139 | var ticket = new Ticket(client); 140 | 141 | do 142 | { 143 | uint ticketId = (uint)RNG.Next(); 144 | 145 | if (validTickets.FirstOrDefault(t => t.Identifier == ticketId) != null) continue; 146 | 147 | ticket.Identifier = ticketId; 148 | validTickets.Add(ticket); 149 | return ticket; 150 | } 151 | while (true); 152 | } 153 | 154 | #if DEBUG 155 | public static Ticket CreateDebugTicket(RiceClient client, uint ticketId) 156 | { 157 | Ticket ticket = new Ticket(client) 158 | { 159 | Identifier = ticketId 160 | }; 161 | validTickets.Add(ticket); 162 | return ticket; 163 | } 164 | #endif 165 | 166 | public static Ticket GetTicket(uint ticketId) 167 | { 168 | return validTickets.FirstOrDefault(t => t.Identifier == ticketId); 169 | } 170 | 171 | public static void InvalidateTicket(uint ticketId) 172 | { 173 | var ticket = GetTicket(ticketId); 174 | if (ticket == null) return; 175 | 176 | validTickets.Remove(ticket); 177 | } 178 | 179 | public static Serial GetSerial(ushort serialId) 180 | { 181 | return validSerials.FirstOrDefault(s => s.Identifier == serialId); 182 | } 183 | 184 | public static void InvalidateSerial(ushort serialId) 185 | { 186 | var serial = GetSerial(serialId); 187 | if (serial == null) return; 188 | 189 | validSerials.Remove(serial); 190 | } 191 | 192 | public static Area GetArea(int id) 193 | { 194 | var area = areas.FirstOrDefault(a => a.ID == id); 195 | if (area != null) return area; 196 | 197 | area = new Area(id); 198 | areas.Add(area); 199 | return area; 200 | } 201 | 202 | public static Area[] GetAreas() 203 | { 204 | return areas.ToArray(); 205 | } 206 | } 207 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Rice.Server.Core; 7 | 8 | namespace Rice.Server.Structures 9 | { 10 | public struct LoadCharAck : ISerializable 11 | { 12 | public uint ServerId; 13 | public uint ServerStartTime; 14 | public CharInfo CharInfo; 15 | public uint nCarSize; 16 | public List CarInfo; 17 | 18 | public void Serialize(PacketWriter writer) 19 | { 20 | writer.Write(ServerId); 21 | writer.Write(ServerStartTime); 22 | CharInfo.Serialize(writer); 23 | writer.Write(nCarSize); 24 | 25 | foreach (CarInfo info in CarInfo) 26 | info.Serialize(writer); 27 | } 28 | } 29 | 30 | public struct ExpInfo : ISerializable 31 | { 32 | public long CurExp; 33 | public long NextExp; 34 | public long BaseExp; 35 | 36 | public void Serialize(PacketWriter writer) 37 | { 38 | writer.Write(CurExp); 39 | writer.Write(NextExp); 40 | writer.Write(BaseExp); 41 | } 42 | 43 | public static ExpInfo FromLevelExp(int level, long exp) 44 | { 45 | return new ExpInfo 46 | { 47 | BaseExp = RiceServer.ExpTable[level - 1], 48 | NextExp = RiceServer.ExpSumTable[level], 49 | CurExp = exp 50 | }; 51 | } 52 | } 53 | 54 | public struct ItemInfo : ISerializable 55 | { 56 | public uint CurCarID; 57 | public ushort State; 58 | public ushort Slot; 59 | public uint StackNum; // it's an int in npluto's implementation for some reason 60 | public uint LastCarID; 61 | public uint AssistA; 62 | public uint AssistB; 63 | public uint AssistC; 64 | public uint AssistD; 65 | public uint AssistE; 66 | public uint AssistF; 67 | public uint AssistG; 68 | public uint AssistH; 69 | public uint AssistI; 70 | public uint AssistJ; 71 | public uint Box; 72 | public uint Belonging; 73 | public int Upgrade; 74 | public int UpgradePoint; 75 | public uint ExpireTick; 76 | public float ItemHealth; 77 | public uint unk1; 78 | public uint TableIdx; 79 | public uint InvenIdx; 80 | public int Random; 81 | 82 | public void Serialize(PacketWriter writer) 83 | { 84 | writer.Write(CurCarID); 85 | writer.Write(State); 86 | writer.Write(Slot); 87 | writer.Write(StackNum); 88 | writer.Write(LastCarID); 89 | writer.Write(AssistA); 90 | writer.Write(AssistB); 91 | writer.Write(AssistC); 92 | writer.Write(AssistD); 93 | writer.Write(AssistE); 94 | writer.Write(AssistF); 95 | writer.Write(AssistG); 96 | writer.Write(AssistH); 97 | writer.Write(AssistI); 98 | writer.Write(AssistJ); 99 | writer.Write(Box); 100 | writer.Write(Belonging); 101 | writer.Write(Upgrade); 102 | writer.Write(UpgradePoint); 103 | writer.Write(ExpireTick); 104 | writer.Write(ItemHealth); 105 | writer.Write(0); // unk1, 0 in all known cases 106 | writer.Write(TableIdx); 107 | writer.Write(InvenIdx); 108 | writer.Write(Random); 109 | } 110 | } 111 | 112 | public struct ItemModInfo : ISerializable 113 | { 114 | public ItemInfo Item; 115 | public int State; 116 | 117 | public void Serialize(PacketWriter writer) 118 | { 119 | Item.Serialize(writer); 120 | writer.Write(State); 121 | } 122 | } 123 | 124 | public struct CharInfo : ISerializable 125 | { 126 | public ulong CID; 127 | public string Name; // 0x15 128 | public string LastMessageFrom; // 0xB 129 | public int LastDate; 130 | public ushort Avatar; 131 | public ushort Level; 132 | public ExpInfo ExpInfo; 133 | public long MitoMoney; 134 | public long TeamId; 135 | public long TeamMarkId; 136 | public string TeamName; // 0xD 137 | public int TeamRank; 138 | public byte PType; 139 | public uint PvpCnt; 140 | public uint PvpWinCnt; 141 | public uint PvpPoint; 142 | public uint TPvpCnt; 143 | public uint TPvpWinCnt; 144 | public uint TPvpPoint; 145 | public uint QuickCnt; 146 | public float TotalDistance; 147 | public Vector4 Position; 148 | public int LastChannel; 149 | public int City; 150 | public int PosState; 151 | public int CurrentCarID; 152 | public uint QuickSlot1; 153 | public uint QuickSlot2; 154 | public DateTime TeamJoinDate; 155 | public DateTime TeamCloseDate; 156 | public DateTime TeamLeaveDate; 157 | public int HancoinInven; 158 | public int HancoinGarage; 159 | public int Flags; 160 | public int Guild; 161 | public long Mileage; 162 | public uint GPTeam; 163 | 164 | public void Serialize(PacketWriter writer) 165 | { 166 | writer.Write(CID); 167 | writer.WriteUnicodeStatic(Name, 21); 168 | writer.WriteUnicodeStatic(LastMessageFrom, 11); 169 | writer.Write(LastDate); 170 | writer.Write(Avatar); 171 | writer.Write(Level); 172 | ExpInfo.Serialize(writer); 173 | writer.Write(MitoMoney); 174 | writer.Write(TeamId); 175 | writer.Write(TeamMarkId); 176 | writer.WriteUnicodeStatic(TeamName, 13); 177 | writer.Write(TeamRank); 178 | writer.Write(PType); 179 | writer.Write(PvpCnt); 180 | writer.Write(PvpWinCnt); 181 | writer.Write(PvpPoint); 182 | writer.Write(TPvpCnt); 183 | writer.Write(TPvpWinCnt); 184 | writer.Write(TPvpPoint); 185 | writer.Write(QuickCnt); 186 | writer.Write(0); // unknown 187 | writer.Write(0); // unknown 188 | writer.Write(TotalDistance); 189 | writer.Write(Position); 190 | writer.Write(LastChannel); 191 | writer.Write(City); 192 | writer.Write(PosState); 193 | writer.Write(CurrentCarID); 194 | writer.Write(QuickSlot1); 195 | writer.Write(QuickSlot2); 196 | writer.Write(TeamJoinDate); 197 | writer.Write(TeamCloseDate); 198 | writer.Write(TeamLeaveDate); 199 | writer.Write(new byte[12]); // filler 200 | writer.Write(HancoinInven); 201 | writer.Write(HancoinGarage); 202 | writer.Write(Flags); 203 | writer.Write(Guild); 204 | writer.Write(new byte[38]); // filler 205 | writer.Write(GPTeam); // DCGP team 206 | } 207 | } 208 | 209 | public struct VisualItem : ISerializable 210 | { 211 | public short Neon; 212 | public short Plate; 213 | public short Decal; 214 | public short DecalColor; 215 | public short AeroBumper; 216 | public short AeroIntercooler; 217 | public short AeroSet; 218 | public short MufflerFlame; 219 | public short Wheel; 220 | public short Spoiler; 221 | public short[] Reserve; // 6 222 | public string PlateString; // 9 223 | 224 | public void Serialize(PacketWriter writer) 225 | { 226 | writer.Write(Neon); 227 | writer.Write(Plate); 228 | writer.Write(Decal); 229 | writer.Write(DecalColor); 230 | writer.Write(AeroBumper); 231 | writer.Write(AeroIntercooler); 232 | writer.Write(AeroSet); 233 | writer.Write(MufflerFlame); 234 | writer.Write(Wheel); 235 | writer.Write(Spoiler); 236 | for (int i = 0; i < Reserve.Length; i++) 237 | writer.Write(Reserve[i]); 238 | writer.WriteUnicodeStatic(PlateString, 9); 239 | } 240 | } 241 | 242 | public struct PlayerInfo : ISerializable 243 | { 244 | public string Name; // 0xD 245 | public ushort Serial; 246 | public ushort Age; 247 | public long Cid; 248 | public ushort Level; 249 | public uint Exp; 250 | public long TeamId; 251 | public long TeamMarkId; 252 | public string TeamName; // 0xE 253 | public ushort TeamNLevel; 254 | public VisualItem VisualItem; 255 | public float UseTime; 256 | 257 | public void Serialize(PacketWriter writer) 258 | { 259 | writer.WriteUnicodeStatic(Name, 0xD, true); 260 | writer.Write(Serial); 261 | writer.Write(Age); 262 | writer.Write(new byte[186]); // filler 263 | /* 264 | writer.Write(Cid); 265 | writer.Write(Level); 266 | writer.Write(Exp); 267 | writer.Write(TeamId); 268 | writer.Write(TeamMarkId); 269 | writer.Write(TeamName); 270 | writer.Write(TeamNLevel); 271 | VisualItem.Serialize(writer); 272 | writer.Write(UseTime); 273 | */ 274 | } 275 | } 276 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Misc.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Rice.Server.Structures 8 | { 9 | public interface ISerializable 10 | { 11 | void Serialize(PacketWriter bw); 12 | } 13 | 14 | public class Vector4 : ISerializable 15 | { 16 | public float X, Y, Z, Rotation; 17 | 18 | public Vector4(float x, float y, float z, float rotation = 0) 19 | { 20 | X = x; 21 | Y = y; 22 | Z = z; 23 | Rotation = rotation; 24 | } 25 | 26 | public void Serialize(PacketWriter bw) 27 | { 28 | bw.Write(X); 29 | bw.Write(Y); 30 | bw.Write(Z); 31 | bw.Write(Rotation); 32 | } 33 | 34 | public static Vector4 Deserialize(PacketReader br) 35 | { 36 | float x = br.ReadSingle(); 37 | float y = br.ReadSingle(); 38 | float z = br.ReadSingle(); 39 | float rot = br.ReadSingle(); 40 | return new Vector4(x, y, z, rot); 41 | } 42 | 43 | public override string ToString() 44 | { 45 | return $"Vec4({X}, {Y}, {Z}, {Rotation})"; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Rice/Server/Structures/Resources/AssistTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Dynamic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using Newtonsoft.Json; 8 | 9 | namespace Rice.Server.Structures.Resources 10 | { 11 | public static class AssistTable 12 | { 13 | public static List Assists = new List(); 14 | 15 | public static void Load(string assistclientpath) 16 | { 17 | if (!File.Exists(assistclientpath)) 18 | { 19 | Log.WriteError("Could not find AssistClient at " + assistclientpath); 20 | return; 21 | } 22 | 23 | string assistClientJson = File.ReadAllText(assistclientpath); 24 | Assists = JsonConvert.DeserializeObject>(assistClientJson); 25 | } 26 | 27 | public static int GetIndex(string id) 28 | { 29 | int idx = Assists.FindIndex(i => i.ID.ToLower() == id.ToLower()); 30 | return idx < 0 ? -1 : (idx + 1); 31 | } 32 | } 33 | 34 | [JsonObject(MemberSerialization.OptIn)] 35 | public class AssistTableEntry 36 | { 37 | [JsonProperty("id")] public string ID { get; set; } 38 | [JsonProperty("function")] public string Function { get; set; } 39 | 40 | [JsonProperty("name")] public string Name { get; set; } 41 | [JsonProperty("description")] public string Description { get; set; } 42 | 43 | // double or n/a 44 | [JsonProperty("stat")] public string Stat { get; set; } 45 | // km/h, point, %, n/a etc 46 | [JsonProperty("unit")] public string Unit { get; set; } 47 | } 48 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Resources/ItemTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using Newtonsoft.Json; 7 | 8 | namespace Rice.Server.Structures.Resources 9 | { 10 | public static class ItemTable 11 | { 12 | public static List Items = new List(); 13 | public static Dictionary SlotMap = new Dictionary(); 14 | 15 | public static void Load(string itempath, string useitempath) 16 | { 17 | if (!File.Exists(itempath)) 18 | { 19 | Log.WriteError("Could not find ItemTable at " + itempath); 20 | return; 21 | } 22 | 23 | if (!File.Exists(useitempath)) 24 | { 25 | Log.WriteError("Could not find UseItemTable at " + useitempath); 26 | return; 27 | } 28 | 29 | string itemjson = File.ReadAllText(itempath); 30 | string useitemjson = File.ReadAllText(useitempath); 31 | Items.AddRange(JsonConvert.DeserializeObject>(itemjson)); 32 | Items.AddRange(JsonConvert.DeserializeObject>(useitemjson)); 33 | 34 | int[] partSlots = {100, 101, 103, 104, 106, 107, 109, 110}; 35 | int[] speedSlots = {100, 101}; 36 | int[] accelSlots = {103, 104}; 37 | int[] duraSlots = {106, 107}; 38 | int[] boostSlots = {109, 110}; 39 | 40 | SlotMap = new Dictionary 41 | { 42 | {"op_F", partSlots}, 43 | 44 | {"speed", speedSlots}, 45 | {"op_S", speedSlots}, 46 | {"dock_S", speedSlots}, 47 | 48 | {"accel", accelSlots}, 49 | {"op_A", accelSlots}, 50 | {"dock_A", accelSlots}, 51 | 52 | {"crash", duraSlots}, 53 | {"op_C", duraSlots}, 54 | {"dock_C", duraSlots}, 55 | 56 | {"boost", boostSlots}, 57 | {"op_B", boostSlots}, 58 | {"dock_B", boostSlots}, 59 | 60 | {"neo_weapon", new [] {112}}, 61 | {"neo_coil", new [] {113}}, 62 | {"neo_fuse", new [] {114}}, 63 | {"neo_armor", new [] {115}} 64 | }; 65 | } 66 | } 67 | 68 | [JsonObject(MemberSerialization.OptIn)] 69 | public class GenericItemEntry 70 | { 71 | [JsonProperty("id")] public string ID; 72 | 73 | [JsonProperty("category")] public string Category; 74 | 75 | [JsonProperty("name")] public string Name; 76 | 77 | [JsonProperty("description")] public string Description; 78 | 79 | [JsonProperty("function")] public string Function; 80 | 81 | [JsonProperty("nextstate")] public string NextState; 82 | 83 | [JsonProperty("buyvalue")] public string BuyValue; 84 | [JsonProperty("sellvalue")] public string SellValue; 85 | 86 | [JsonProperty("expirationtime")] public string ExpirationTime; 87 | 88 | [JsonProperty("auctionable")] public string Auctionable; 89 | 90 | [JsonProperty("partsshop")] public string PartsShop; 91 | 92 | [JsonProperty("sendable")] public string Sendable; 93 | 94 | virtual public bool IsStackable() 95 | { 96 | return false; 97 | } 98 | 99 | virtual public uint GetMaxStack() 100 | { 101 | return 1; 102 | } 103 | 104 | public bool IsSellable() => SellValue.ToLower() != "n/a"; 105 | } 106 | 107 | public class ItemTableEntry : GenericItemEntry 108 | { 109 | [JsonProperty("setid")] public string SetID; 110 | 111 | [JsonProperty("setname")] public string SetName; 112 | 113 | [JsonProperty("grade")] public string Grade; 114 | 115 | [JsonProperty("minlevel")] public string MinLevel; 116 | 117 | [JsonProperty("basepoints")] public string BasePoints; 118 | 119 | [JsonProperty("basepointmodifier")] public string BasePointModifier; 120 | 121 | [JsonProperty("basepointvariable")] public string BasePointVariable; 122 | 123 | [JsonProperty("partassist")] public string PartAssist; 124 | 125 | [JsonProperty("lube")] public string Lube; 126 | 127 | [JsonProperty("neostats")] public string NeoStats; 128 | } 129 | 130 | public class UseItemTableEntry : GenericItemEntry 131 | { 132 | [JsonProperty("maxstack")] public string MaxStack; 133 | 134 | [JsonProperty("stat")] public string StatModifier; 135 | 136 | [JsonProperty("cooldown")] public string CooldownTime; 137 | 138 | [JsonProperty("duration")] public string Duration; 139 | 140 | override public bool IsStackable() 141 | { 142 | return Category != "car"; 143 | } 144 | 145 | override public uint GetMaxStack() 146 | { 147 | if (MaxStack == "n/a" || MaxStack == "0") 148 | return 99; 149 | return Convert.ToUInt32(MaxStack); 150 | } 151 | } 152 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Resources/QuestTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using Newtonsoft.Json; 7 | 8 | namespace Rice.Server.Structures.Resources 9 | { 10 | public static class QuestTable 11 | { 12 | public static List Quests; 13 | 14 | public static void Load(string path) 15 | { 16 | if (!File.Exists(path)) 17 | { 18 | Log.WriteError("Could not find QuestTable at " + path); 19 | return; 20 | } 21 | 22 | string json = File.ReadAllText(path); 23 | Quests = JsonConvert.DeserializeObject>(json); 24 | } 25 | } 26 | 27 | [JsonObject(MemberSerialization.OptIn)] 28 | public class QuestTableEntry 29 | { 30 | [JsonProperty("id")] public string ID; 31 | 32 | [JsonProperty("missiontype")] public string MissionType; 33 | 34 | [JsonProperty("tableindex")] public int TableIndex; 35 | 36 | [JsonProperty("title")] public string Title; 37 | 38 | [JsonProperty("introprompt")] public string IntroPrompt; 39 | [JsonProperty("station")] public string StartStation; 40 | 41 | [JsonProperty("completestation")] public string CompleteStation; 42 | [JsonProperty("completeprompt")] public string CompletePrompt; 43 | 44 | [JsonProperty("objective1")] public string Objective1; 45 | [JsonProperty("objective2")] public string Objective2; 46 | [JsonProperty("objective3")] public string Objective3; 47 | [JsonProperty("objective4")] public string Objective4; 48 | [JsonProperty("objective5")] public string Objective5; 49 | 50 | [JsonProperty("huvlevel")] public int HuvLevel; 51 | [JsonProperty("huvid")] public int HuvID; 52 | 53 | [JsonProperty("exp")] public int Experience; 54 | [JsonProperty("mito")] public int Mito; 55 | 56 | [JsonProperty("reward1")] public string RewardItem1; 57 | [JsonProperty("reward2")] public string RewardItem2; 58 | [JsonProperty("reward3")] public string RewardItem3; 59 | 60 | public override string ToString() 61 | { 62 | return Title; 63 | } 64 | 65 | public int GetObjectiveCount() 66 | { 67 | int count = 0; 68 | count += Objective1 != "0" ? 1 : 0; 69 | count += Objective2 != "0" ? 1 : 0; 70 | count += Objective3 != "0" ? 1 : 0; 71 | count += Objective4 != "0" ? 1 : 0; 72 | count += Objective5 != "0" ? 1 : 0; 73 | return count; 74 | } 75 | 76 | public string[] GetRewards() 77 | { 78 | var rewards = new List(); 79 | if (RewardItem1 != "0") 80 | rewards.Add(RewardItem1); 81 | if (RewardItem2 != "0") 82 | rewards.Add(RewardItem2); 83 | if (RewardItem3 != "0") 84 | rewards.Add(RewardItem3); 85 | return rewards.ToArray(); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Resources/VehicleTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using Newtonsoft.Json; 7 | 8 | namespace Rice.Server.Structures.Resources 9 | { 10 | public static class VehicleTable 11 | { 12 | public static Dictionary> VehicleUpgrades = new Dictionary>(); 13 | public static Dictionary Vehicles = new Dictionary(); 14 | 15 | public static void Load(string vehicleupgradepath, string vehiclelistpath) 16 | { 17 | if (!File.Exists(vehicleupgradepath)) 18 | { 19 | Log.WriteError("Could not find VehicleUpgrade at " + vehicleupgradepath); 20 | return; 21 | } 22 | 23 | if (!File.Exists(vehiclelistpath)) 24 | { 25 | Log.WriteError("Could not find VehicleList at " + vehiclelistpath); 26 | return; 27 | } 28 | 29 | string vehicleUpgradeJson = File.ReadAllText(vehicleupgradepath); 30 | VehicleUpgrades = JsonConvert.DeserializeObject>>(vehicleUpgradeJson); 31 | 32 | string vehicleListJson = File.ReadAllText(vehiclelistpath); 33 | Vehicles = JsonConvert.DeserializeObject>(vehicleListJson); 34 | 35 | foreach (var carId in VehicleUpgrades.Keys) 36 | foreach (var upgrade in VehicleUpgrades[carId]) 37 | upgrade.CarID = carId; 38 | } 39 | 40 | public static VehicleUpgradeEntry GetVehicleUpgrade(int sort, int grade) 41 | { 42 | if (grade < 1 || grade > 9) 43 | throw new Exception($"Invalid vehicle grade {grade} specified"); 44 | 45 | if (!VehicleUpgrades.ContainsKey(sort)) 46 | throw new Exception($"No vehicle with sort {sort} found"); 47 | 48 | var entry = VehicleUpgrades[sort][grade - 1]; 49 | 50 | if (!entry.IsValidGrade()) 51 | throw new Exception($"Grade {grade} is not valid for vehicle {sort}"); 52 | 53 | return entry; 54 | } 55 | } 56 | 57 | [JsonObject(MemberSerialization.OptIn)] 58 | public class VehicleUpgradeEntry 59 | { 60 | public int CarID { get; set; } 61 | 62 | [JsonProperty("price")] public int Price; 63 | [JsonProperty("sell")] public int SellPrice; 64 | 65 | [JsonProperty("game_ui_name")] public string Name; 66 | 67 | [JsonProperty("gradelvl")] public int GradeLevel; 68 | [JsonProperty("gradetype")] public string GradeType; 69 | 70 | [JsonProperty("upgrademito")] public int UpgradeCost; 71 | 72 | [JsonProperty("capacity")] public float MitronCapacity; 73 | [JsonProperty("efficienty")] public float MitronEfficiency; 74 | 75 | public bool IsValidGrade() => Name.Length > 0; 76 | } 77 | 78 | [JsonObject(MemberSerialization.OptIn)] 79 | public class VehicleListEntry 80 | { 81 | [JsonProperty("unique_id")] public int CarID { get; set; } 82 | 83 | [JsonProperty("sellable")] public int Sellable; 84 | [JsonProperty("lvl")] public int Level; 85 | 86 | [JsonProperty("req_cond")] public string Condition; 87 | 88 | [JsonProperty("file_name")] public string FileName; 89 | 90 | public bool InDealership() => Sellable == 1; 91 | public bool HasCondition() => Condition != "N/A"; 92 | 93 | // doesn't look like there's a more reliable way 94 | public string GetKeyId() => FileName.Split('.')[0]; 95 | } 96 | } -------------------------------------------------------------------------------- /Rice/Server/Structures/Vehicle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Rice.Server.Structures 8 | { 9 | public struct CarInfo : ISerializable 10 | { 11 | public CarUnit CarUnit; 12 | public uint Color; 13 | public uint Color2; 14 | public float MitronCapacity; 15 | public float MitronEfficiency; 16 | public bool AuctionOn; 17 | public bool SBBOn; 18 | 19 | public void Serialize(PacketWriter writer) 20 | { 21 | CarUnit.Serialize(writer); 22 | writer.Write(Color); 23 | writer.Write(Color2); 24 | writer.Write(MitronCapacity); 25 | writer.Write(MitronEfficiency); 26 | writer.Write(AuctionOn); 27 | writer.Write(SBBOn); 28 | } 29 | } 30 | 31 | public struct CarUnit : ISerializable 32 | { 33 | public int CarID; 34 | public uint CarType; 35 | public uint BaseColor; 36 | public uint Grade; 37 | public uint SlotType; 38 | public uint AuctionCnt; 39 | public float Mitron; 40 | public float Kmh; 41 | 42 | public void Serialize(PacketWriter writer) 43 | { 44 | writer.Write(CarID); 45 | writer.Write(CarType); 46 | writer.Write(BaseColor); 47 | writer.Write(Grade); 48 | writer.Write(SlotType); 49 | writer.Write(AuctionCnt); 50 | writer.Write(Mitron); 51 | writer.Write(Kmh); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Rice/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Rice 8 | { 9 | public class Utilities 10 | { 11 | public static string MD5(string str) 12 | { 13 | byte[] strBuf = Encoding.ASCII.GetBytes(str); 14 | byte[] hash = System.Security.Cryptography.MD5.Create().ComputeHash(strBuf); 15 | 16 | return BitConverter.ToString(hash).Replace("-", "").ToLower(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Rice/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------