├── .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 |
--------------------------------------------------------------------------------