45 Commits
0.3a ... 0.4a

Author SHA1 Message Date
5a455d0487 Auto fetch master server address 2022-06-03 16:28:02 +08:00
11d178498f Back to old rotation calculation. 2022-06-03 14:40:41 +08:00
88a51cc154 Add radio station sync. 2022-06-03 13:11:17 +08:00
291ba5691b Request WeaponAsset before swapping weapon. 2022-06-02 16:40:38 +08:00
de63cd1271 Add Tank muzzle flash 2022-06-02 15:51:58 +08:00
8479502d75 Fix scramjet 2022-06-02 14:10:29 +08:00
051b496021 Two muzzles fix 2022-06-02 13:52:18 +08:00
2c6f0dbe3d Don't delete player's vehicle 2022-06-02 13:11:01 +08:00
ffb73c1ce4 Fix savage 2022-06-02 13:09:13 +08:00
c941ca96fc Projectile and stromberg fix. 2022-06-02 12:44:05 +08:00
e2d4ce2159 Weapon fix 2022-06-02 10:34:33 +08:00
a500a4eeed closes #4, add scramjet. 2022-06-02 09:27:39 +08:00
603e8af746 closes #5, add Vigilante. 2022-06-02 09:26:53 +08:00
0ae4e1c122 closes #6, add Apocalypse 380Z 2022-06-02 09:25:23 +08:00
83f260048d closes #7, add Future Shock 380Z. 2022-06-02 09:24:37 +08:00
9b79956c71 closes #8, add Nightmare 380Z 2022-06-02 09:23:44 +08:00
82c6dab4c4 add stromberg 2022-06-02 09:21:46 +08:00
6611ecac18 closes #10, add apocalypse 2022-06-02 09:17:19 +08:00
3fa62f1a69 closes #3, add savage 2022-06-02 09:15:18 +08:00
ad3031ef59 Logger fix 2022-06-02 08:53:01 +08:00
42d057e2c5 Latency update and threading fixes. 2022-06-01 19:05:45 +08:00
87b9a67d0b Update assemby version 2022-06-01 18:01:38 +08:00
4fca28f0e0 Ah~ 2022-06-01 17:57:54 +08:00
8ebf03ca99 Hmm... 2022-06-01 17:57:27 +08:00
f2760b1533 Merge branch 'main' of https://github.com/RAGECOOP/RageCoop-V 2022-06-01 17:56:02 +08:00
7eec12486f country road, take me home... 2022-06-01 17:55:38 +08:00
d789e4cb0b Update README.md 2022-06-01 16:27:50 +08:00
485497286f Update README.md
some shit
2022-06-01 16:27:10 +08:00
a305fb349f Update README.md 2022-06-01 16:06:02 +08:00
7aa41a6e0c Master server is back! 2022-05-31 23:12:32 -08:00
9d9d421671 Small fix 2022-05-31 19:46:40 -08:00
a8a16fac61 Scripting? 2022-05-31 19:35:01 -08:00
b863b7037a Added ability to disable automatic respawn
Prepare for client side scripting.
2022-05-31 02:16:12 -08:00
a33f839004 opt 2022-05-31 10:11:06 +08:00
a48170fec8 EnterVehicle fix, reduce NPC 2022-05-31 09:55:54 +08:00
2181a52d6c Add Tula 2022-05-31 09:24:28 +08:00
b96d349e4a Lots of sh*t
API cleanup
Complete deluxo transformation sync
blahblahblah
2022-05-31 09:14:30 +08:00
ad698a656d Removed LVector, LQuaternion.
Server side optimization.
Nework optimization (streaming distance).
2022-05-30 14:32:38 +08:00
aefea337f1 Some optimization. 2022-05-30 11:11:40 +08:00
811e7df14e fix for changing owner. 2022-05-29 18:27:07 +08:00
5cc63e688f Merge branch 'main' of https://github.com/RAGECOOP/RageCoop-V 2022-05-29 18:16:44 +08:00
08aa1370de Add nozzle and deluxo transformation sync. 2022-05-29 18:16:32 +08:00
5d6b99442b Update README.md 2022-05-27 17:08:40 +08:00
65b695cb78 Update README.md 2022-05-27 17:06:41 +08:00
ce94a9c7af Update README.md 2022-05-27 17:01:31 +08:00
61 changed files with 2852 additions and 2015 deletions

View File

@ -11,14 +11,15 @@ namespace RageCoop.Client
/// </summary>
public static class COOPAPI
{
#region DELEGATES
/// <summary>
/// ?
/// </summary>
/// <param name="connected"></param>
/// <param name="from">The Lidgren-Network net handle</param>
/// <param name="from">The player's id</param>
/// <param name="reason"></param>
public delegate void ConnectEvent(bool connected, long from, string reason = null);
public delegate void ConnectEvent(bool connected, int from, string reason = null);
/// <summary>
/// ?
/// </summary>
@ -45,10 +46,6 @@ namespace RageCoop.Client
/// ?
/// </summary>
public static event ChatMessage OnChatMessage;
/// <summary>
/// ?
/// </summary>
public static event ModEvent OnModPacketReceived;
public static void Connected()
{
@ -60,19 +57,14 @@ namespace RageCoop.Client
OnConnection?.Invoke(false, GetPlayerID(), reason);
}
public static void Connected(long netHandle)
public static void Connected(int playerID)
{
OnConnection?.Invoke(true, netHandle);
OnConnection?.Invoke(true, playerID);
}
public static void Disconnected(long netHandle)
public static void Disconnected(int playerID)
{
OnConnection?.Invoke(false, netHandle);
}
public static void ModPacketReceived(long from, string mod, byte customID, byte[] bytes)
{
OnModPacketReceived?.Invoke(from, mod, customID, bytes);
OnConnection?.Invoke(false, playerID);
}
public static bool ChatMessageReceived(string from, string message)
@ -99,7 +91,7 @@ namespace RageCoop.Client
/// <param name="serverAddress">The server address to connect. Example: 127.0.0.1:4499</param>
public static void Connect(string serverAddress)
{
Networking.DisConnectFromServer(serverAddress);
Networking.ToggleConnection(serverAddress);
}
/// <summary>
@ -107,7 +99,7 @@ namespace RageCoop.Client
/// </summary>
public static void Disconnect()
{
Networking.DisConnectFromServer(null);
Networking.ToggleConnection(null);
}
/// <summary>
@ -122,25 +114,11 @@ namespace RageCoop.Client
/// Get the local player's ID
/// </summary>
/// <returns>PlayerID</returns>
public static long GetPlayerID()
public static int GetPlayerID()
{
return Main.LocalPlayerID;
}
/*
/// <summary>
/// Get a player using their Lidgren Network net handle
/// </summary>
/// <param name="handle">Lidgren-Network net handle</param>
public static CharacterEntity GetPed(int ID)
{
lock (Main.Characters)
{
return Main.Characters.ContainsKey(ID) ? Main.Characters[ID] : null;
}
}
*/
/// <summary>
/// Check if a RAGECOOP menu is visible
/// </summary>
@ -149,7 +127,7 @@ namespace RageCoop.Client
#if NON_INTERACTIVE
return false;
#else
return Main.MainMenu.MenuPool.AreAnyVisible;
return Menus.CoopMenu.MenuPool.AreAnyVisible;
#endif
}
@ -177,40 +155,6 @@ namespace RageCoop.Client
return Main.CurrentVersion;
}
/// <summary>
/// Send any data (bytes) to the server
/// </summary>
/// <param name="modName">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToServer(string modName, byte customID, byte[] bytes)
{
Networking.SendModData(-1, modName, customID, bytes);
}
/// <summary>
/// Send any data (bytes) to the all player
/// </summary>
/// <param name="modName">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToAll(string modName, byte customID, byte[] bytes)
{
Networking.SendModData(0, modName, customID, bytes);
}
/// <summary>
/// Send any data (bytes) to a player
/// </summary>
/// <param name="netHandle">The Lidgren Network net handle that receives the data</param>
/// <param name="modName">The name of this modification (script)</param>
/// <param name="customID">The ID to know what the data is</param>
/// <param name="bytes">Your class, structure or whatever in bytes</param>
public static void SendDataToPlayer(long netHandle, string modName, byte customID, byte[] bytes)
{
Networking.SendModData(netHandle, modName, customID, bytes);
}
/// <summary>
/// Get that player's local username
/// </summary>
@ -236,35 +180,13 @@ namespace RageCoop.Client
return true;
}
/// <summary>
/// Enable or disable the local traffic for this player
/// Get or set the client's settings.
/// </summary>
/// <param name="enable">true to disable traffic</param>
public static void SetLocalTraffic(bool enable)
/// <returns>The client's settings, you should NEVER change settings without notifying the player.</returns>
public static Settings Settings()
{
Main.Settings.DisableTraffic = !enable;
return Main.Settings;
}
/// <summary>
/// Sets the alignment for the player list, if set to true it will align left,
/// otherwise it will align right
/// </summary>
/// <param name="leftAlign">true to move the player list to the left</param>
public static void SetPlayerListLeftAlign(bool leftAlign)
{
PlayerList.LeftAlign = leftAlign;
}
#if DEBUG
/// <summary>
/// ?
/// </summary>
/// <param name="value"></param>
public static void SetDebug(bool value)
{
Main.UseDebug = value;
}
#endif
}
}

View File

@ -130,7 +130,7 @@ namespace RageCoop.Client
s=$@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
i=Main.Ticked%2==0 ? {Current} : {Secondary};
i=BulletsShot%2==0 ? {Current} : {Secondary};
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].{dir}Vector);
";
}
@ -139,7 +139,7 @@ namespace RageCoop.Client
s=$@"
// {ToMark.DisplayName}
case {ToMark.Model.Hash}:
i=Main.Ticked%2==0 ? {Current} : {Secondary};
i=BulletsShot%2==0 ? {Current} : {Secondary};
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].{((MuzzleDir)(dir-3)).ToString()}Vector*-1);
";
}

View File

@ -21,25 +21,22 @@ namespace RageCoop.Client
{
private bool _gameLoaded = false;
private static bool _isGoingToCar = false;
public static readonly string CurrentVersion = "V0_2";
private bool _lastDead = false;
public static readonly string CurrentVersion = "V0_4";
public static int LocalPlayerID=0;
public static bool NPCsAllowed = false;
internal static RelationshipGroup SyncedPedsGroup;
public static new Settings Settings = null;
#if !NON_INTERACTIVE
public static RageCoopMenu MainMenu = null;
#endif
public static Chat MainChat = null;
public static Stopwatch Counter = new Stopwatch();
public static Core.Logging.Logger Logger = null;
public static ulong Ticked = 0;
public static Loggger Logger=new Loggger("Scripts\\RageCoop\\RageCoop.Client.log");
private static List<Func<bool>> QueuedActions = new List<Func<bool>>();
/// <summary>
@ -47,7 +44,16 @@ namespace RageCoop.Client
/// </summary>
public Main()
{
Logger=new Core.Logging.Logger()
{
LogPath="Scripts\\RageCoop\\RageCoop.Client.log",
UseConsole=false,
#if DEBUG
LogLevel = 0,
#else
LogLevel=Settings.LogLevel,
#endif
};
// Required for some synchronization!
/*if (Game.Version < GameVersion.v1_0_1290_1_Steam)
{
@ -57,7 +63,7 @@ namespace RageCoop.Client
{
return;
}
if (!_gameLoaded)
{
GTA.UI.Notification.Show("~r~Please update your GTA5 to v1.0.1290 or newer!", true);
@ -71,14 +77,8 @@ namespace RageCoop.Client
Settings = Util.ReadSettings();
Networking.Start();
#if !NON_INTERACTIVE
MainMenu = new RageCoopMenu();
#endif
MainChat = new Chat();
#if DEBUG
Logger.LogLevel = 0;
#else
Logger.LogLevel=Settings.LogLevel;
#endif
Tick += OnTick;
KeyDown += OnKeyDown;
@ -107,14 +107,9 @@ namespace RageCoop.Client
}
#if !NON_INTERACTIVE
MainMenu.MenuPool.Process();
CoopMenu.MenuPool.Process();
#endif
if (_isGoingToCar && Game.Player.Character.IsInVehicle())
{
_isGoingToCar = false;
}
DoQueuedActions();
if (!Networking.IsOnServer)
{
@ -130,7 +125,7 @@ namespace RageCoop.Client
}
catch (Exception ex)
{
Logger.Error(ex);
Main.Logger.Error(ex);
}
if (!DownloadManager.DownloadComplete)
@ -164,9 +159,32 @@ namespace RageCoop.Client
MainChat.Tick();
PlayerList.Tick();
if (Settings.DisableAutoRespawn)
{
Function.Call(Hash.PAUSE_DEATH_ARREST_RESTART, true);
Function.Call(Hash.FORCE_GAME_STATE_PLAYING);
var P = Game.Player.Character;
if (P.IsDead)
{
Function.Call(Hash.TERMINATE_ALL_SCRIPTS_WITH_THIS_NAME, "respawn_controller");
Function.Call(Hash.SET_FADE_OUT_AFTER_DEATH, false);
if (P.Health!=1)
{
P.Health=1;
Game.Player.WantedLevel=0;
Main.Logger.Debug("Player died.");
}
GTA.UI.Screen.StopEffects();
}
else
{
Function.Call(Hash.DISPLAY_HUD, true);
}
_lastDead=P.IsDead;
}
Ticked++;
}
@ -193,14 +211,20 @@ namespace RageCoop.Client
}
if (e.KeyCode == Settings.MenuKey)
{
if (MainMenu.MenuPool.AreAnyVisible)
if (CoopMenu.MenuPool.AreAnyVisible)
{
MainMenu.MainMenu.Visible = false;
MainMenu.SubSettings.Menu.Visible = false;
CoopMenu.MenuPool.ForEach<LemonUI.Menus.NativeMenu>(x =>
{
if (x.Visible)
{
CoopMenu.LastMenu=x;
x.Visible=false;
}
});
}
else
{
MainMenu.MainMenu.Visible = true;
CoopMenu.LastMenu.Visible = true;
}
}
else if (Game.IsControlJustPressed(GTA.Control.MultiplayerInfo))
@ -234,7 +258,7 @@ namespace RageCoop.Client
if (V!=null)
{
var seat = Util.GetNearestSeat(P, V);
var seat = P.GetNearestSeat(V);
P.Task.EnterVehicle(V, seat);
}
}
@ -311,7 +335,7 @@ namespace RageCoop.Client
catch
{
GTA.UI.Notification.Show("~r~~h~CleanUpWorld() Error");
Logger.Error($"CleanUpWorld(): ~r~Item {item.Value} cannot be deleted!");
Main.Logger.Error($"CleanUpWorld(): ~r~Item {item.Value} cannot be deleted!");
}
}
@ -379,7 +403,7 @@ namespace RageCoop.Client
// s+=$"\r\n{c.IsAiming} {c.IsJumping} {c.IsOnFire} {c.IsOnLadder} {c.IsRagdoll} {c.IsReloading} {c.IsShooting} {c.Speed}";
}
}
Logger.Trace(s);
Main.Logger.Trace(s);
return s;
}
public static string DumpPlayers()
@ -390,7 +414,7 @@ namespace RageCoop.Client
s+=$"\r\nID:{p.PedID} Username:{p.Username}";
}
Logger.Trace(s);
Main.Logger.Trace(s);
return s;
}

114
Client/Menus/CoopMenu.cs Normal file
View File

@ -0,0 +1,114 @@
using GTA;
using System.Drawing;
using LemonUI;
using LemonUI.Menus;
namespace RageCoop.Client.Menus
{
/// <summary>
/// Don't use it!
/// </summary>
internal static class CoopMenu
{
public static ObjectPool MenuPool = new ObjectPool();
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "MAIN")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
public static NativeMenu LastMenu { get; set; } = Menu;
#region ITEMS
private static readonly NativeItem _usernameItem = new NativeItem("Username") { AltTitle = Main.Settings.Username };
public static readonly NativeItem ServerIpItem = new NativeItem("Server IP") { AltTitle = Main.Settings.LastServerAddress };
private static readonly NativeItem _serverConnectItem = new NativeItem("Connect");
private static readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" +
"https://github.com/RAGECOOP~n~" +
"~y~VERSION~s~~n~" +
Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") };
#endregion
/// <summary>
/// Don't use it!
/// </summary>
static CoopMenu()
{
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0);
_usernameItem.Activated += UsernameActivated;
ServerIpItem.Activated += ServerIpActivated;
_serverConnectItem.Activated += (sender, item) => { Networking.ToggleConnection(Main.Settings.LastServerAddress); };
Menu.AddSubMenu(ServersMenu.Menu);
Menu.Add(_usernameItem);
Menu.Add(ServerIpItem);
Menu.Add(_serverConnectItem);
Menu.AddSubMenu(SettingsMenu.Menu);
Menu.AddSubMenu(DevToolMenu.Menu);
Menu.AddSubMenu(DebugMenu.Menu);
MenuPool.Add(Menu);
MenuPool.Add(SettingsMenu.Menu);
MenuPool.Add(DevToolMenu.Menu);
MenuPool.Add(DebugMenu.Menu);
MenuPool.Add(DebugMenu.DiagnosticMenu);
MenuPool.Add(ServersMenu.Menu);
Menu.Add(_aboutItem);
}
public static void UsernameActivated(object a, System.EventArgs b)
{
string newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, _usernameItem.AltTitle, 20);
if (!string.IsNullOrWhiteSpace(newUsername))
{
Main.Settings.Username = newUsername;
Util.SaveSettings();
_usernameItem.AltTitle = newUsername;
}
}
public static void ServerIpActivated(object a, System.EventArgs b)
{
string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
if (!string.IsNullOrWhiteSpace(newServerIp) && newServerIp.Contains(":"))
{
Main.Settings.LastServerAddress = newServerIp;
Util.SaveSettings();
ServerIpItem.AltTitle = newServerIp;
}
}
public static void InitiateConnectionMenuSetting()
{
_usernameItem.Enabled = false;
ServerIpItem.Enabled = false;
_serverConnectItem.Enabled = false;
}
public static void ConnectedMenuSetting()
{
_serverConnectItem.Enabled = true;
_serverConnectItem.Title = "Disconnect";
Menu.Visible = false;
}
public static void DisconnectedMenuSetting()
{
_usernameItem.Enabled = true;
ServerIpItem.Enabled = true;
_serverConnectItem.Enabled = true;
_serverConnectItem.Title = "Connect";
}
}
}

View File

@ -1,115 +0,0 @@
using GTA;
using System.Drawing;
using LemonUI;
using LemonUI.Menus;
namespace RageCoop.Client.Menus
{
/// <summary>
/// Don't use it!
/// </summary>
public class RageCoopMenu
{
public ObjectPool MenuPool = new ObjectPool();
public NativeMenu MainMenu = new NativeMenu("RAGECOOP", "MAIN")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
#region SUB
public Sub.SettingsMenu SubSettings = new Sub.SettingsMenu();
#endregion
#region ITEMS
private readonly NativeItem _usernameItem = new NativeItem("Username") { AltTitle = Main.Settings.Username };
public readonly NativeItem ServerIpItem = new NativeItem("Server IP") { AltTitle = Main.Settings.LastServerAddress };
private readonly NativeItem _serverConnectItem = new NativeItem("Connect");
private readonly NativeItem _aboutItem = new NativeItem("About", "~y~SOURCE~s~~n~" +
"https://github.com/RAGECOOP~n~" +
"~y~VERSION~s~~n~" +
Main.CurrentVersion.Replace("_", ".")) { LeftBadge = new LemonUI.Elements.ScaledTexture("commonmenu", "shop_new_star") };
#endregion
/// <summary>
/// Don't use it!
/// </summary>
public RageCoopMenu()
{
MainMenu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
MainMenu.Title.Color = Color.FromArgb(255, 165, 0);
_usernameItem.Activated += UsernameActivated;
ServerIpItem.Activated += ServerIpActivated;
_serverConnectItem.Activated += (sender, item) => { Networking.DisConnectFromServer(Main.Settings.LastServerAddress); };
MainMenu.Add(_usernameItem);
MainMenu.Add(ServerIpItem);
MainMenu.Add(_serverConnectItem);
MainMenu.Add(_aboutItem);
MainMenu.AddSubMenu(SubSettings.Menu);
MainMenu.AddSubMenu(DevToolMenu.Menu);
MainMenu.AddSubMenu(DebugMenu.Menu);
MenuPool.Add(MainMenu);
MenuPool.Add(SubSettings.Menu);
MenuPool.Add(DevToolMenu.Menu);
MenuPool.Add(DebugMenu.Menu);
MenuPool.Add(DebugMenu.DiagnosticMenu);
}
public void UsernameActivated(object a, System.EventArgs b)
{
string newUsername = Game.GetUserInput(WindowTitle.EnterMessage20, _usernameItem.AltTitle, 20);
if (!string.IsNullOrWhiteSpace(newUsername))
{
Main.Settings.Username = newUsername;
Util.SaveSettings();
_usernameItem.AltTitle = newUsername;
}
}
public void ServerIpActivated(object a, System.EventArgs b)
{
string newServerIp = Game.GetUserInput(WindowTitle.EnterMessage60, ServerIpItem.AltTitle, 60);
if (!string.IsNullOrWhiteSpace(newServerIp) && newServerIp.Contains(":"))
{
Main.Settings.LastServerAddress = newServerIp;
Util.SaveSettings();
ServerIpItem.AltTitle = newServerIp;
}
}
public void InitiateConnectionMenuSetting()
{
MainMenu.Items[0].Enabled = false;
MainMenu.Items[1].Enabled = false;
MainMenu.Items[2].Enabled = false;
}
public void ConnectedMenuSetting()
{
MainMenu.Items[2].Enabled = true;
MainMenu.Items[2].Title = "Disconnect";
MainMenu.Visible = false;
}
public void DisconnectedMenuSetting()
{
MainMenu.Items[0].Enabled = true;
MainMenu.Items[1].Enabled = true;
MainMenu.Items[2].Enabled = true;
MainMenu.Items[2].Title = "Connect";
SubSettings.Menu.Items[1].Enabled = false;
}
}
}

View File

@ -29,7 +29,7 @@ namespace RageCoop.Client
d1.Activated+=(sender,e) =>
{
try{ SyncParameters.PositioinPrediction =float.Parse(Game.GetUserInput(WindowTitle.EnterMessage20, SyncParameters.PositioinPrediction.ToString(), 20));}
try{ SyncParameters.PositioinPredictionDefault =float.Parse(Game.GetUserInput(WindowTitle.EnterMessage20, SyncParameters.PositioinPredictionDefault.ToString(), 20));}
catch { }
Update();
};
@ -55,7 +55,7 @@ namespace RageCoop.Client
private static void Update()
{
d1.AltTitle = SyncParameters.PositioinPrediction.ToString();
d1.AltTitle = SyncParameters.PositioinPredictionDefault.ToString();
}
}
}

View File

@ -9,7 +9,7 @@ using System.Drawing;
namespace RageCoop.Client
{
internal class DevToolMenu
internal static class DevToolMenu
{
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "DevTool", "Help with the development")
{

View File

@ -0,0 +1,148 @@
using System;
using System.Net;
using System.Drawing;
using System.Collections.Generic;
using Newtonsoft.Json;
using LemonUI.Menus;
using System.Threading;
namespace RageCoop.Client.Menus
{
internal class ServerListClass
{
[JsonProperty("address")]
public string Address { get; set; }
[JsonProperty("port")]
public string Port { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("version")]
public string Version { get; set; }
[JsonProperty("players")]
public int Players { get; set; }
[JsonProperty("maxPlayers")]
public int MaxPlayers { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
}
/// <summary>
/// Don't use it!
/// </summary>
internal static class ServersMenu
{
private static Thread GetServersThread;
internal static NativeMenu Menu = new NativeMenu("RAGECOOP", "Servers", "Go to the server list")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
internal static NativeItem ResultItem = null;
/// <summary>
/// Don't use it!
/// </summary>
static ServersMenu()
{
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0);
Menu.Opening += (object sender, System.ComponentModel.CancelEventArgs e) =>
{
CleanUpList();
Menu.Add(ResultItem = new NativeItem("Loading..."));
// Prevent freezing
GetServersThread=new Thread(()=> GetAllServers());
GetServersThread.Start();
};
Menu.Closing += (object sender, System.ComponentModel.CancelEventArgs e) =>
{
CleanUpList();
};
}
private static void CleanUpList()
{
Menu.Clear();
ResultItem = null;
}
private static void GetAllServers()
{
List<ServerListClass> serverList = null;
var realUrl = Main.Settings.MasterServer=="[AUTO]" ? DownloadString("https://ragecoop.online/stuff/masterserver") : Main.Settings.MasterServer;
serverList = JsonConvert.DeserializeObject<List<ServerListClass>>(DownloadString(realUrl));
// Need to be processed in main thread
Main.QueueAction(() =>
{
if (serverList == null)
{
ResultItem.Title = "Something went wrong!";
return;
}
if (serverList.Count == 0)
{
ResultItem.Title = "No server was found!";
return;
}
CleanUpList();
foreach (ServerListClass server in serverList)
{
string address = $"{server.Address}:{server.Port}";
NativeItem tmpItem = new NativeItem($"[{server.Country}] {server.Name}", $"~b~{address}~s~~n~~g~Version {server.Version}.x~s~") { AltTitle = $"[{server.Players}/{server.MaxPlayers}]" };
tmpItem.Activated += (object sender, EventArgs e) =>
{
try
{
Menu.Visible = false;
Networking.ToggleConnection(address);
#if !NON_INTERACTIVE
CoopMenu.ServerIpItem.AltTitle = address;
CoopMenu.Menu.Visible = true;
#endif
Main.Settings.LastServerAddress = address;
Util.SaveSettings();
}
catch (Exception ex)
{
GTA.UI.Notification.Show($"~r~{ex.Message}");
}
};
Menu.Add(tmpItem);
}
});
}
private static string DownloadString(string url)
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new WebClient();
return client.DownloadString(url);
}
catch (Exception ex)
{
Main.QueueAction(() =>
{
ResultItem.Title = "Download failed!";
ResultItem.Description = ex.Message;
});
return "";
}
}
}
}

View File

@ -4,24 +4,24 @@ using System.Windows.Forms;
using GTA;
using LemonUI.Menus;
namespace RageCoop.Client.Menus.Sub
namespace RageCoop.Client.Menus
{
/// <summary>
/// Don't use it!
/// </summary>
public class SettingsMenu
internal static class SettingsMenu
{
public NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings")
public static NativeMenu Menu = new NativeMenu("RAGECOOP", "Settings", "Go to the settings")
{
UseMouse = false,
Alignment = Main.Settings.FlipMenu ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left
};
private readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic);
private readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
private readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableTraffic);
private static readonly NativeCheckboxItem _disableTrafficItem = new NativeCheckboxItem("Disable Traffic (NPCs/Vehicles)", "Local traffic only", Main.Settings.DisableTraffic);
private static readonly NativeCheckboxItem _flipMenuItem = new NativeCheckboxItem("Flip menu", Main.Settings.FlipMenu);
private static readonly NativeCheckboxItem _disablePauseAlt = new NativeCheckboxItem("Disable Alternate Pause", "Don't freeze game time when Esc pressed", Main.Settings.DisableTraffic);
private readonly NativeCheckboxItem _showNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
private static readonly NativeCheckboxItem _showNetworkInfoItem = new NativeCheckboxItem("Show Network Info", Networking.ShowNetworkInfo);
private static NativeItem _menuKey = new NativeItem("Menu Key","The key to open menu", Main.Settings.MenuKey.ToString());
private static NativeItem _passengerKey = new NativeItem("Passenger Key", "The key to enter a vehicle as passenger", Main.Settings.PassengerKey.ToString());
@ -30,7 +30,7 @@ namespace RageCoop.Client.Menus.Sub
/// <summary>
/// Don't use it!
/// </summary>
public SettingsMenu()
static SettingsMenu()
{
Menu.Banner.Color = Color.FromArgb(225, 0, 0, 0);
Menu.Title.Color = Color.FromArgb(255, 165, 0);
@ -52,14 +52,14 @@ namespace RageCoop.Client.Menus.Sub
Menu.Add(_vehicleSoftLimit);
}
private void _disablePauseAlt_CheckboxChanged(object sender, EventArgs e)
private static void _disablePauseAlt_CheckboxChanged(object sender, EventArgs e)
{
Main.Settings.DisableAlternatePause=_disablePauseAlt.Checked;
Util.SaveSettings();
}
private void vehicleSoftLimit_Activated(object sender, EventArgs e)
private static void vehicleSoftLimit_Activated(object sender, EventArgs e)
{
try
{
@ -71,7 +71,7 @@ namespace RageCoop.Client.Menus.Sub
}
catch { }
}
private void ChaneMenuKey(object sender, EventArgs e)
private static void ChaneMenuKey(object sender, EventArgs e)
{
try
{
@ -85,7 +85,7 @@ namespace RageCoop.Client.Menus.Sub
catch { }
}
private void ChangePassengerKey(object sender, EventArgs e)
private static void ChangePassengerKey(object sender, EventArgs e)
{
try
{
@ -99,22 +99,22 @@ namespace RageCoop.Client.Menus.Sub
catch { }
}
public void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
public static void DisableTrafficCheckboxChanged(object a, System.EventArgs b)
{
Main.Settings.DisableTraffic = _disableTrafficItem.Checked;
Util.SaveSettings() ;
}
public void FlipMenuCheckboxChanged(object a, System.EventArgs b)
public static void FlipMenuCheckboxChanged(object a, System.EventArgs b)
{
Main.MainMenu.MainMenu.Alignment = _flipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
CoopMenu.Menu.Alignment = _flipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
Menu.Alignment = _flipMenuItem.Checked ? GTA.UI.Alignment.Right : GTA.UI.Alignment.Left;
Main.Settings.FlipMenu = _flipMenuItem.Checked;
Util.SaveSettings();
}
public void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
public static void ShowNetworkInfoCheckboxChanged(object a, System.EventArgs b)
{
Networking.ShowNetworkInfo = _showNetworkInfoItem.Checked;

View File

@ -1,903 +0,0 @@
using System;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using RageCoop.Core;
using GTA;
using GTA.Native;
using GTA.Math;
using System.Linq;
using System.Diagnostics;
namespace RageCoop.Client
{
/// <summary>
///
/// </summary>
internal static partial class Util
{
#region -- POINTER --
private static int _steeringAngleOffset { get; set; }
public static unsafe void NativeMemory()
{
IntPtr address;
address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
if (address != IntPtr.Zero)
{
_steeringAngleOffset = *(int*)(address + 6) + 8;
}
// breaks some stuff.
/*
address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != IntPtr.Zero)
{
for (int i = 0; i < 6; i++)
{
*(byte*)(address + i).ToPointer() = 0x90;
}
}
*/
}
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
{
IntPtr address = new IntPtr((long)veh.MemoryAddress);
if (address == IntPtr.Zero || _steeringAngleOffset == 0)
{
return;
}
*(float*)(address + _steeringAngleOffset).ToPointer() = value;
}
#endregion
public static Settings ReadSettings()
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
Settings settings = null;
if (File.Exists(path))
{
using (FileStream stream = File.OpenRead(path))
{
settings = (RageCoop.Client.Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
}
}
else
{
using (FileStream stream = File.OpenWrite(path))
{
ser.Serialize(stream, settings = new Settings());
}
}
return settings;
}
public static void SaveSettings()
{
try
{
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings);
}
}
catch (Exception ex)
{
GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
}
}
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0 && Comparer<T>.Default.Compare(item, end) <= 0;
}
public static bool Compare<T, Y>(this Dictionary<T, Y> item, Dictionary<T, Y> item2)
{
if (item == null || item2 == null || item.Count != item2.Count)
{
return false;
}
foreach (KeyValuePair<T, Y> pair in item)
{
if (item2.TryGetValue(pair.Key, out Y value) && Equals(value, pair.Value))
{
continue;
}
// TryGetValue() or Equals failed
return false;
}
// No difference between item and item2
return true;
}
#region MATH
public static Vector3 LinearVectorLerp(Vector3 start, Vector3 end, ulong currentTime, int duration)
{
return new Vector3()
{
X = LinearFloatLerp(start.X, end.X, currentTime, duration),
Y = LinearFloatLerp(start.Y, end.Y, currentTime, duration),
Z = LinearFloatLerp(start.Z, end.Z, currentTime, duration),
};
}
public static float LinearFloatLerp(float start, float end, ulong currentTime, int duration)
{
return (end - start) * currentTime / duration + start;
}
public static float Lerp(float from, float to, float fAlpha)
{
return (from * (1.0f - fAlpha)) + (to * fAlpha); //from + (to - from) * fAlpha
}
public static Vector3 RotationToDirection(Vector3 rotation)
{
double z = DegToRad(rotation.Z);
double x = DegToRad(rotation.X);
double num = Math.Abs(Math.Cos(x));
return new Vector3
{
X = (float)(-Math.Sin(z) * num),
Y = (float)(Math.Cos(z) * num),
Z = (float)Math.Sin(x)
};
}
#endregion
public static Model ModelRequest(this int hash)
{
Model model = new Model(hash);
if (!model.IsValid)
{
//GTA.UI.Notification.Show("~y~Not valid!");
return null;
}
if (!model.IsLoaded)
{
return model.Request(1000) ? model : null;
}
return model;
}
#region PED
public static byte GetPedSpeed(this Ped ped)
{
if (ped.IsSprinting)
{
return 3;
}
if (ped.IsRunning)
{
return 2;
}
if (ped.IsWalking)
{
return 1;
}
return 0;
}
public static Dictionary<byte, short> GetPedClothes(this Ped ped)
{
Dictionary<byte, short> result = new Dictionary<byte, short>();
for (byte i = 0; i < 11; i++)
{
short mod = Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
result.Add(i, mod);
}
return result;
}
public static PedDataFlags GetPedFlags(this Ped ped)
{
PedDataFlags flags = PedDataFlags.None;
if (ped.IsAiming || ped.IsOnTurretSeat())
{
flags |= PedDataFlags.IsAiming;
}
if (ped.IsReloading)
{
flags |= PedDataFlags.IsReloading;
}
if (ped.IsJumping)
{
flags |= PedDataFlags.IsJumping;
}
if (ped.IsRagdoll)
{
flags |= PedDataFlags.IsRagdoll;
}
if (ped.IsOnFire)
{
flags |= PedDataFlags.IsOnFire;
}
if (ped.IsInParachuteFreeFall)
{
flags |= PedDataFlags.IsInParachuteFreeFall;
}
if (ped.ParachuteState == ParachuteState.Gliding)
{
flags |= PedDataFlags.IsParachuteOpen;
}
bool climbingLadder = ped.IsTaskActive(TaskType.CTaskGoToAndClimbLadder);
if (climbingLadder)
{
flags |= PedDataFlags.IsOnLadder;
}
if (ped.IsVaulting && !climbingLadder)
{
flags |= PedDataFlags.IsVaulting;
}
if (ped.IsInCover || ped.IsGoingIntoCover)
{
flags |=PedDataFlags.IsInCover;
}
return flags;
}
public static string[] GetReloadingAnimation(this Ped ped)
{
switch (ped.Weapons.Current.Hash)
{
case WeaponHash.Revolver:
case WeaponHash.RevolverMk2:
case WeaponHash.DoubleActionRevolver:
case WeaponHash.NavyRevolver:
return new string[2] { "anim@weapons@pistol@revolver_str", "reload_aim" };
case WeaponHash.APPistol:
return new string[2] { "weapons@pistol@ap_pistol_str", "reload_aim" };
case WeaponHash.Pistol50:
return new string[2] { "weapons@pistol@pistol_50_str", "reload_aim" };
case WeaponHash.Pistol:
case WeaponHash.PistolMk2:
case WeaponHash.PericoPistol:
case WeaponHash.SNSPistol:
case WeaponHash.SNSPistolMk2:
case WeaponHash.HeavyPistol:
case WeaponHash.VintagePistol:
case WeaponHash.CeramicPistol:
case WeaponHash.MachinePistol:
return new string[2] { "weapons@pistol@pistol_str", "reload_aim" };
case WeaponHash.AssaultRifle:
case WeaponHash.AssaultrifleMk2:
return new string[2] { "weapons@rifle@hi@assault_rifle_str", "reload_aim" };
case WeaponHash.SniperRifle:
return new string[2] { "weapons@rifle@hi@sniper_rifle_str", "reload_aim" };
case WeaponHash.HeavySniper:
case WeaponHash.HeavySniperMk2:
return new string[2] { "weapons@rifle@lo@sniper_heavy_str", "reload_aim" };
case WeaponHash.PumpShotgun:
case WeaponHash.PumpShotgunMk2:
return new string[2] { "weapons@rifle@pump_str", "reload_aim" };
case WeaponHash.Railgun:
return new string[2] { "weapons@rifle@lo@rail_gun_str", "reload_aim" };
case WeaponHash.SawnOffShotgun:
return new string[2] { "weapons@rifle@lo@sawnoff_str", "reload_aim" };
case WeaponHash.AssaultShotgun:
return new string[2] { "weapons@rifle@lo@shotgun_assault_str", "reload_aim" };
case WeaponHash.BullpupShotgun:
return new string[2] { "weapons@rifle@lo@shotgun_bullpup_str", "reload_aim" };
case WeaponHash.AdvancedRifle:
return new string[2] { "weapons@submg@advanced_rifle_str", "reload_aim" };
case WeaponHash.CarbineRifle:
case WeaponHash.CarbineRifleMk2:
case WeaponHash.CompactRifle:
return new string[2] { "weapons@rifle@lo@carbine_str", "reload_aim" };
case WeaponHash.Gusenberg:
return new string[2] { "anim@weapons@machinegun@gusenberg_str", "reload_aim" };
case WeaponHash.Musket:
return new string[2] { "anim@weapons@musket@musket_str", "reload_aim" };
case WeaponHash.FlareGun:
return new string[2] { "anim@weapons@pistol@flare_str", "reload_aim" };
case WeaponHash.SpecialCarbine:
case WeaponHash.SpecialCarbineMk2:
return new string[2] { "anim@weapons@rifle@lo@spcarbine_str", "reload_aim" };
case WeaponHash.CombatPDW:
return new string[2] { "anim@weapons@rifle@lo@pdw_str", "reload_aim" };
case WeaponHash.BullpupRifle:
case WeaponHash.BullpupRifleMk2:
return new string[2] { "anim@weapons@submg@bullpup_rifle_str", "reload_aim" };
case WeaponHash.AssaultSMG:
return new string[2] { "weapons@submg@assault_smg_str", "reload_aim" };
case WeaponHash.MicroSMG:
case WeaponHash.MiniSMG:
return new string[2] { "weapons@submg@lo@micro_smg_str", "reload_aim" };
case WeaponHash.SMG:
case WeaponHash.SMGMk2:
return new string[2] { "weapons@rifle@smg_str", "reload_aim" };
case WeaponHash.GrenadeLauncher:
case WeaponHash.GrenadeLauncherSmoke:
case WeaponHash.CompactGrenadeLauncher:
return new string[2] { "weapons@heavy@grenade_launcher_str", "reload_aim" };
case WeaponHash.RPG:
case WeaponHash.Firework:
return new string[2] { "weapons@heavy@rpg_str", "reload_aim" };
case WeaponHash.CombatMG:
case WeaponHash.CombatMGMk2:
return new string[2] { "weapons@machinegun@combat_mg_str", "reload_aim" };
case WeaponHash.MG:
return new string[2] { "weapons@machinegun@mg_str", "reload_aim" };
default:
Main.Logger.Warning($"~r~Reloading failed! Weapon ~g~[{ped.Weapons.Current.Hash}]~r~ could not be found!");
return null;
}
}
public static VehicleSeat GetNearestSeat(Ped ped, Vehicle veh, float distanceToignoreDoors = 50f)
{
float num = 99f;
int result = -2;
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("door_dside_f", -1);
dictionary.Add("door_pside_f", 0);
dictionary.Add("door_dside_r", 1);
dictionary.Add("door_pside_r", 2);
foreach (string text in dictionary.Keys)
{
bool flag = veh.Bones[text].Position != Vector3.Zero;
if (flag)
{
float num2 = ped.Position.DistanceTo(Function.Call<Vector3>(Hash.GET_WORLD_POSITION_OF_ENTITY_BONE, new InputArgument[]
{
veh,
veh.Bones[text].Index
}));
bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num)&& IsSeatUsableByPed(ped, veh, dictionary[text]);
if (flag2)
{
num = num2;
result = dictionary[text];
}
}
}
return (VehicleSeat)result;
}
public static bool IsSeatUsableByPed(Ped ped, Vehicle veh, int _seat)
{
VehicleSeat seat = (VehicleSeat)_seat;
bool result = false;
bool flag = veh.IsSeatFree(seat);
if (flag)
{
result = true;
}
else
{
bool isDead = veh.GetPedOnSeat(seat).IsDead;
if (isDead)
{
result = true;
}
else
{
int num = Function.Call<int>(Hash.GET_RELATIONSHIP_BETWEEN_PEDS, new InputArgument[]
{
ped,
veh.GetPedOnSeat(seat)
});
bool flag2 = num > 2;
if (flag2)
{
result = true;
}
}
}
return result;
}
public static bool IsTaskActive(this Ped p,TaskType task)
{
return Function.Call<bool>(Hash.GET_IS_TASK_ACTIVE, p.Handle, task);
}
public static Vector3 GetAimCoord(this Ped p)
{
var weapon = p.Weapons.CurrentWeaponObject;
var v = p.CurrentVehicle;
// Rhino
if (v!=null && v.Model.Hash==782665360)
{
return v.Bones[35].Position+v.Bones[35].ForwardVector*100;
}
if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); }
if (weapon!=null)
{
// Not very accurate, but doesn't matter
Vector3 dir = weapon.RightVector;
return weapon.Position+dir*20;
}
return GetLookingCoord(p);
}
public static Vector3 GetLookingCoord(this Ped p)
{
EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized;
return b.Position+200*v;
}
public static void StayInCover(this Ped p)
{
Function.Call(Hash.TASK_STAY_IN_COVER, p);
}
public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{
return (VehicleSeat)Function.Call<int>(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p);
}
#endregion
#region VEHICLE
public static VehicleDataFlags GetVehicleFlags(this Vehicle veh)
{
VehicleDataFlags flags = 0;
if (veh.IsEngineRunning)
{
flags |= VehicleDataFlags.IsEngineRunning;
}
if (veh.AreLightsOn)
{
flags |= VehicleDataFlags.AreLightsOn;
}
if (veh.BrakePower >= 0.01f)
{
flags |= VehicleDataFlags.AreBrakeLightsOn;
}
if (veh.AreHighBeamsOn)
{
flags |= VehicleDataFlags.AreHighBeamsOn;
}
if (veh.IsSirenActive)
{
flags |= VehicleDataFlags.IsSirenActive;
}
if (veh.IsDead)
{
flags |= VehicleDataFlags.IsDead;
}
if (Function.Call<bool>(Hash.IS_HORN_ACTIVE, veh.Handle))
{
flags |= VehicleDataFlags.IsHornActive;
}
if (veh.IsSubmarineCar && Function.Call<bool>(Hash._GET_IS_SUBMARINE_VEHICLE_TRANSFORMED, veh.Handle))
{
flags |= VehicleDataFlags.IsTransformed;
}
if (veh.HasRoof && (veh.RoofState == VehicleRoofState.Opened || veh.RoofState == VehicleRoofState.Opening))
{
flags |= VehicleDataFlags.RoofOpened;
}
if (veh.IsAircraft)
{
flags |= VehicleDataFlags.IsAircraft;
}
return flags;
}
public static bool HasFlag(this PedDataFlags flagToCheck,PedDataFlags flag)
{
return (flagToCheck & flag)!=0;
}
public static bool HasFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
{
return (flagToCheck & flag)!=0;
}
public static Dictionary<uint, bool> GetWeaponComponents(this Weapon weapon)
{
Dictionary<uint, bool> result = null;
if (weapon.Components.Count > 0)
{
result = new Dictionary<uint, bool>();
foreach (var comp in weapon.Components)
{
result.Add((uint)comp.ComponentHash, comp.Active);
}
}
return result;
}
public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
{
Dictionary<int, int> result = new Dictionary<int, int>();
foreach (VehicleMod mod in mods.ToArray())
{
result.Add((int)mod.Type, mod.Index);
}
return result;
}
public static VehicleDamageModel GetVehicleDamageModel(this Vehicle veh)
{
// Broken windows
byte brokenWindows = 0;
for (int i = 0; i < 8; i++)
{
if (!veh.Windows[(VehicleWindowIndex)i].IsIntact)
{
brokenWindows |= (byte)(1 << i);
}
}
// Broken doors
byte brokenDoors = 0;
byte openedDoors = 0;
foreach (VehicleDoor door in veh.Doors)
{
if (door.IsBroken)
{
brokenDoors |= (byte)(1 << (byte)door.Index);
}
else if (door.IsOpen)
{
openedDoors |= (byte)(1 << (byte)door.Index);
}
}
// Bursted tires
short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
{
if (wheel.IsBursted)
{
burstedTires |= (short)(1 << (int)wheel.BoneId);
}
}
return new VehicleDamageModel()
{
BrokenDoors = brokenDoors,
OpenedDoors = openedDoors,
BrokenWindows = brokenWindows,
BurstedTires = burstedTires,
LeftHeadLightBroken = (byte)(veh.IsLeftHeadLightBroken ? 1 : 0),
RightHeadLightBroken = (byte)(veh.IsRightHeadLightBroken ? 1 : 0)
};
}
public static Dictionary<int,int> GetPassengers(this Vehicle veh)
{
Dictionary<int,int> ps=new Dictionary<int, int>();
var d = veh.Driver;
if (d!=null&&d.IsSittingInVehicle())
{
ps.Add(-1, d.GetSyncEntity().ID);
}
foreach(Ped p in veh.Passengers)
{
if (p.IsSittingInVehicle())
{
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID);
}
}
return ps;
}
public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true)
{
for (int i = 0; i < 8; i++)
{
var door = veh.Doors[(VehicleDoorIndex)i];
if ((model.BrokenDoors & (byte)(1 << i)) != 0)
{
door.Break(leavedoors);
}
else if (door.IsBroken)
{
// The vehicle can only fix a door if the vehicle was completely fixed
veh.Repair();
return;
}
if ((model.OpenedDoors & (byte)(1 << i)) != 0)
{
if ((!door.IsOpen)&&(!door.IsBroken))
{
door.Open();
}
}
else if (door.IsOpen)
{
if (!door.IsBroken) { door.Close(); }
}
if ((model.BrokenWindows & (byte)(1 << i)) != 0)
{
veh.Windows[(VehicleWindowIndex)i].Smash();
}
else if (!veh.Windows[(VehicleWindowIndex)i].IsIntact)
{
veh.Windows[(VehicleWindowIndex)i].Repair();
}
}
foreach (VehicleWheel wheel in veh.Wheels)
{
if ((model.BurstedTires & (short)(1 << (int)wheel.BoneId)) != 0)
{
if (!wheel.IsBursted)
{
wheel.Puncture();
wheel.Burst();
}
}
else if (wheel.IsBursted)
{
wheel.Fix();
}
}
veh.IsLeftHeadLightBroken = model.LeftHeadLightBroken > 0;
veh.IsRightHeadLightBroken = model.RightHeadLightBroken > 0;
}
#endregion
public static void SetOnFire(this Entity e,bool toggle)
{
if (toggle)
{
Function.Call(Hash.START_ENTITY_FIRE, e.Handle);
}
else
{
Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle);
}
}
public static SyncedPed GetSyncEntity(this Ped p)
{
if(p == null) { return null; }
var c = EntityPool.GetPedByHandle(p.Handle);
if(c==null) { EntityPool.Add(c=new SyncedPed(p)); }
return c;
}
public static SyncedVehicle GetSyncEntity(this Vehicle veh)
{
if(veh == null) { return null; }
var v=EntityPool.GetVehicleByHandle(veh.Handle);
if (v==null) { EntityPool.Add(v=new SyncedVehicle(veh)); }
return v;
}
public static double DegToRad(double deg)
{
return deg * Math.PI / 180.0;
}
public static bool IsTurretSeat(this Vehicle veh, int seat)
{
if (!Function.Call<bool>(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle))
{
return false;
}
switch (seat)
{
case -1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Rhino
|| (VehicleHash)veh.Model.Hash == VehicleHash.Khanjari
|| (VehicleHash)veh.Model.Hash == VehicleHash.FireTruck
|| (VehicleHash)veh.Model.Hash == VehicleHash.Riot2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
case 0:
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc;
case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical3
|| (VehicleHash)veh.Model.Hash == VehicleHash.HalfTrack
|| (VehicleHash)veh.Model.Hash == VehicleHash.Barrage;
case 2:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Barrage;
case 3:
return (VehicleHash)veh.Model.Hash == VehicleHash.Limo2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Dinghy5;
case 7:
return (VehicleHash)veh.Model.Hash == VehicleHash.Insurgent;
}
return false;
}
public static bool IsOnTurretSeat(this Ped P)
{
if (P.CurrentVehicle == null) { return false; }
return IsTurretSeat(P.CurrentVehicle, (int)P.SeatIndex);
}
[DllImport("kernel32.dll")]
public static extern ulong GetTickCount64();
}
/// <summary>
///
/// </summary>
public static class VectorExtensions
{
/// <summary>
///
/// </summary>
public static Vector3 ToVector(this Quaternion vec)
{
return new Vector3()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z
};
}
/// <summary>
///
/// </summary>
public static Quaternion ToQuaternion(this Vector3 vec, float vW = 0.0f)
{
return new Quaternion()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z,
W = vW
};
}
public static float Denormalize(this float h)
{
return h < 0f ? h + 360f : h;
}
public static float ToRadians(this float val)
{
return (float)(Math.PI / 180) * val;
}
public static Vector3 ToRadians(this Vector3 i)
{
return new Vector3()
{
X = ToRadians(i.X),
Y = ToRadians(i.Y),
Z = ToRadians(i.Z),
};
}
public static Quaternion ToQuaternion(this Vector3 vect)
{
vect = new Vector3()
{
X = vect.X.Denormalize() * -1,
Y = vect.Y.Denormalize() - 180f,
Z = vect.Z.Denormalize() - 180f,
};
vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2);
float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin((double)yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2);
Quaternion result = new Quaternion()
{
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
Y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2,
Z = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2,
W = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2
};
return result;
}
/// <summary>
///
/// </summary>
public static LVector3 ToLVector(this Vector3 vec)
{
return new LVector3()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z
};
}
/// <summary>
///
/// </summary>
public static LQuaternion ToLQuaternion(this Quaternion vec)
{
return new LQuaternion()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z,
W = vec.W
};
}
}
}

View File

@ -8,7 +8,7 @@ using GTA.Native;
namespace RageCoop.Client
{
public class Chat
internal class Chat
{
private readonly Scaleform MainScaleForm;

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace RageCoop.Client
{
public static class DownloadManager
internal static class DownloadManager
{
private static readonly List<DownloadFile> _downloadFiles = new List<DownloadFile>();
private static readonly Dictionary<byte, FileStream> _streams = new Dictionary<byte, FileStream>();

View File

@ -6,7 +6,7 @@ using Lidgren.Network;
using RageCoop.Core;
using System.Threading.Tasks;
using System.Threading;
using GTA;
using GTA.Math;
using GTA.Native;
namespace RageCoop.Client
@ -20,7 +20,7 @@ namespace RageCoop.Client
public static int BytesSend = 0;
private static Thread ReceiveThread;
public static void DisConnectFromServer(string address)
public static void ToggleConnection(string address)
{
if (IsOnServer)
{
@ -31,7 +31,7 @@ namespace RageCoop.Client
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new NetPeerConfiguration("623c92c287cc392406e7aaaac1c0f3b0")
{
AutoFlushSendQueue = true
AutoFlushSendQueue = false
};
config.EnableMessageType(NetIncomingMessageType.ConnectionLatencyUpdated);
@ -62,7 +62,6 @@ namespace RageCoop.Client
PedID = Main.LocalPlayerID,
Username = Main.Settings.Username,
ModVersion = Main.CurrentVersion,
NPCsAllowed = false
}.Pack(outgoingMessage);
Client.Connect(ip[0], short.Parse(ip[1]), outgoingMessage);
@ -80,7 +79,11 @@ namespace RageCoop.Client
{
try
{
ReceiveMessages();
if (Client!=null)
{
Client.FlushSendQueue();
ReceiveMessages();
}
}
catch (Exception ex)
{
@ -144,8 +147,8 @@ namespace RageCoop.Client
case string _:
arguments.Add((string)x);
break;
case LVector3 _:
LVector3 vector = (LVector3)x;
case Vector3 _:
Vector3 vector = (Vector3)x;
arguments.Add((float)vector.X);
arguments.Add((float)vector.Y);
arguments.Add((float)vector.Z);
@ -173,7 +176,7 @@ namespace RageCoop.Client
case 0x03: // string
return Function.Call<string>((Hash)hash, arguments.ToArray());
case 0x04: // vector3
return Function.Call<GTA.Math.Vector3>((Hash)hash, arguments.ToArray()).ToLVector();
return Function.Call<Vector3>((Hash)hash, arguments.ToArray());
default:
GTA.UI.Notification.Show("[DecodeNativeCall][" + hash + "]: Type of return not found!");
return null;

View File

@ -5,6 +5,7 @@ using System.Diagnostics;
using Lidgren.Network;
using RageCoop.Core;
using GTA;
using RageCoop.Client.Menus;
using GTA.Math;
using GTA.Native;
@ -36,7 +37,7 @@ namespace RageCoop.Client
{
case NetConnectionStatus.InitiatedConnect:
#if !NON_INTERACTIVE
Main.MainMenu.InitiateConnectionMenuSetting();
CoopMenu.InitiateConnectionMenuSetting();
#endif
Main.QueueAction(() => { GTA.UI.Notification.Show("~y~Trying to connect..."); return true; });
break;
@ -53,17 +54,13 @@ namespace RageCoop.Client
Packets.Handshake handshakePacket = new Packets.Handshake();
handshakePacket.Unpack(data);
// Main.LocalNetHandle = handshakePacket.NetHandle;
Main.NPCsAllowed = handshakePacket.NPCsAllowed;
#if !NON_INTERACTIVE
#endif
COOPAPI.Connected();
Main.QueueAction(() => {
Main.MainMenu.ConnectedMenuSetting();
CoopMenu.ConnectedMenuSetting();
Main.MainChat.Init();
PlayerList.Cleanup();
GTA.UI.Notification.Show("~g~Connected!");
@ -78,9 +75,7 @@ namespace RageCoop.Client
// Reset all values
Latency = 0;
Main.QueueAction(() => { Main.CleanUpWorld();});
Main.NPCsAllowed = false;
Main.QueueAction(() => Main.CleanUpWorld());
if (Main.MainChat.Focused)
{
@ -90,7 +85,7 @@ namespace RageCoop.Client
Main.QueueAction(() => Main.CleanUp());
#if !NON_INTERACTIVE
Main.MainMenu.DisconnectedMenuSetting();
CoopMenu.DisconnectedMenuSetting();
#endif
COOPAPI.Disconnected(reason);
@ -227,8 +222,7 @@ namespace RageCoop.Client
{
Packets.Mod packet = new Packets.Mod();
packet.Unpack(data);
COOPAPI.ModPacketReceived(packet.NetHandle, packet.Name, packet.CustomPacketID, packet.Bytes);
// Need to do some stuff here
}
break;
case PacketTypes.FileTransferTick:
@ -307,20 +301,19 @@ namespace RageCoop.Client
private static void PedSync(Packets.PedSync packet)
{
if (!EntityPool.PedExists(packet.ID))
SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null)
{
Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(new SyncedPed(packet.ID));
EntityPool.ThreadSafe.Add(c=new SyncedPed(packet.ID));
}
PedDataFlags flags = packet.Flag;
SyncedPed c = EntityPool.GetPedByID(packet.ID);
c.ID=packet.ID;
//c.OwnerID=packet.OwnerID;
c.Health = packet.Health;
c.Position = packet.Position.ToVector();
c.Rotation = packet.Rotation.ToVector();
c.Velocity = packet.Velocity.ToVector();
c.Position = packet.Position;
c.Rotation = packet.Rotation;
c.Velocity = packet.Velocity;
c.Speed = packet.Speed;
c.CurrentWeaponHash = packet.CurrentWeaponHash;
c.IsAiming = flags.HasFlag(PedDataFlags.IsAiming);
@ -337,53 +330,47 @@ namespace RageCoop.Client
c.LastSynced = Main.Ticked;
if (c.IsAiming)
{
c.AimCoords = packet.AimCoords.ToVector();
c.AimCoords = packet.AimCoords;
}
if (c.IsRagdoll)
{
c.RotationVelocity=packet.RotationVelocity.ToVector();
c.RotationVelocity=packet.RotationVelocity;
}
}
private static void PedStateSync(Packets.PedStateSync packet)
{
if (!EntityPool.PedExists(packet.ID))
{
Main.Logger.Debug($"Creating character for incoming sync:{packet.ID}");
EntityPool.ThreadSafe.Add(new SyncedPed(packet.ID));
}
SyncedPed c = EntityPool.GetPedByID(packet.ID);
if (c==null) { return; }
c.ID=packet.ID;
c.OwnerID=packet.OwnerID;
c.Clothes=packet.Clothes;
c.WeaponComponents=packet.WeaponComponents;
c.ModelHash=packet.ModelHash;
c.LastSynced=c.LastStateSynced = Main.Ticked;
c.LastStateSynced = Main.Ticked;
}
private static void VehicleSync(Packets.VehicleSync packet)
{
if (!EntityPool.VehicleExists(packet.ID))
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null)
{
EntityPool.ThreadSafe.Add(new SyncedVehicle(packet.ID));
EntityPool.ThreadSafe.Add(v=new SyncedVehicle(packet.ID));
}
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v.IsMine) { return; }
v.ID= packet.ID;
v.Position=packet.Position.ToVector();
v.Rotation=packet.Rotation.ToVector();
v.Position=packet.Position;
v.Quaternion=packet.Quaternion;
v.SteeringAngle=packet.SteeringAngle;
v.ThrottlePower=packet.ThrottlePower;
v.BrakePower=packet.BrakePower;
v.Velocity=packet.Velocity.ToVector();
v.RotationVelocity=packet.RotationVelocity.ToVector();
v.Velocity=packet.Velocity;
v.RotationVelocity=packet.RotationVelocity;
v.DeluxoWingRatio=packet.DeluxoWingRatio;
v.LastSynced=Main.Ticked;
}
private static void VehicleStateSync(Packets.VehicleStateSync packet)
{
if (!EntityPool.VehicleExists(packet.ID))
{
EntityPool.ThreadSafe.Add(new SyncedVehicle(packet.ID));
}
SyncedVehicle v = EntityPool.GetVehicleByID(packet.ID);
if (v==null||v.IsMine) { return; }
v.ID= packet.ID;
v.OwnerID= packet.OwnerID;
v.DamageModel=packet.DamageModel;
@ -403,6 +390,8 @@ namespace RageCoop.Client
v.Transformed = packet.Flag.HasFlag(VehicleDataFlags.IsTransformed);
v.Passengers=new Dictionary<VehicleSeat, SyncedPed>();
v.LockStatus=packet.LockStatus;
v.RadioStation=packet.RadioStation;
v.Flags=packet.Flag;
foreach (KeyValuePair<int, int> pair in packet.Passengers)
{
if (EntityPool.PedExists(pair.Value))
@ -410,7 +399,7 @@ namespace RageCoop.Client
v.Passengers.Add((VehicleSeat)pair.Key, EntityPool.GetPedByID(pair.Value));
}
}
v.LastStateSynced=v.LastSynced= Main.Ticked;
v.LastStateSynced= Main.Ticked;
}
private static void ProjectileSync(Packets.ProjectileSync packet)
@ -422,9 +411,9 @@ namespace RageCoop.Client
Main.Logger.Debug($"Creating new projectile: {(WeaponHash)packet.WeaponHash}");
EntityPool.ThreadSafe.Add(p=new SyncedProjectile(packet.ID));
}
p.Position=packet.Position.ToVector();
p.Rotation=packet.Rotation.ToVector();
p.Velocity=packet.Velocity.ToVector();
p.Position=packet.Position;
p.Rotation=packet.Rotation;
p.Velocity=packet.Velocity;
p.Hash=(WeaponHash)packet.WeaponHash;
p.ShooterID=packet.ShooterID;
p.Exploded=packet.Exploded;

View File

@ -36,9 +36,9 @@ namespace RageCoop.Client
{
ID =c.ID,
Health = p.Health,
Position = p.Position.ToLVector(),
Rotation = p.Rotation.ToLVector(),
Velocity = p.Velocity.ToLVector(),
Position = p.Position,
Rotation = p.Rotation,
Velocity = p.Velocity,
Speed = p.GetPedSpeed(),
CurrentWeaponHash = (uint)p.Weapons.Current.Hash,
Flag = p.GetPedFlags(),
@ -46,11 +46,11 @@ namespace RageCoop.Client
};
if (packet.Flag.HasFlag(PedDataFlags.IsAiming))
{
packet.AimCoords = p.GetAimCoord().ToLVector();
packet.AimCoords = p.GetAimCoord();
}
if (packet.Flag.HasFlag(PedDataFlags.IsRagdoll))
{
packet.RotationVelocity=p.RotationVelocity.ToLVector();
packet.RotationVelocity=p.RotationVelocity;
}
Send(packet, ConnectionChannel.PedSync);
}
@ -76,13 +76,15 @@ namespace RageCoop.Client
{
ID =v.ID,
SteeringAngle = veh.SteeringAngle,
Position = veh.Position.ToLVector(),
Rotation = veh.Rotation.ToLVector(),
Velocity = veh.Velocity.ToLVector(),
RotationVelocity=veh.RotationVelocity.ToLVector(),
Position = veh.PredictPosition(),
Quaternion=veh.Quaternion,
// Rotation = veh.Rotation,
Velocity = veh.Velocity,
RotationVelocity=veh.RotationVelocity,
ThrottlePower = veh.ThrottlePower,
BrakePower = veh.BrakePower,
};
if (v.MainVehicle.Model.Hash==1483171323) { packet.DeluxoWingRatio=v.MainVehicle.GetDeluxoWingRatio(); }
Send(packet,ConnectionChannel.VehicleSync);
}
public static void SendVehicleState(SyncedVehicle v)
@ -108,6 +110,10 @@ namespace RageCoop.Client
Passengers=veh.GetPassengers(),
LockStatus=veh.LockStatus,
};
if (v.MainVehicle==Game.Player.LastVehicle)
{
packet.RadioStation=Util.GetPlayerRadioIndex();
}
Send(packet, ConnectionChannel.VehicleSync);
}
public static void SendProjectile(SyncedProjectile sp)
@ -117,9 +123,9 @@ namespace RageCoop.Client
{
ID =sp.ID,
ShooterID=sp.ShooterID,
Position=p.Position.ToLVector(),
Rotation=p.Rotation.ToLVector(),
Velocity=p.Velocity.ToLVector(),
Position=p.Position,
Rotation=p.Rotation,
Velocity=p.Velocity,
WeaponHash=(uint)p.WeaponHash,
Exploded=p.IsDead
};
@ -133,8 +139,8 @@ namespace RageCoop.Client
{
Send(new Packets.BulletShot()
{
StartPosition = start.ToLVector(),
EndPosition = end.ToLVector(),
StartPosition = start,
EndPosition = end,
OwnerID = ownerID,
WeaponHash=weapon,
}, ConnectionChannel.SyncEvents);
@ -149,27 +155,6 @@ namespace RageCoop.Client
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Chat);
Client.FlushSendQueue();
#if DEBUG
if (ShowNetworkInfo)
{
BytesSend += outgoingMessage.LengthBytes;
}
#endif
}
public static void SendModData(long target, string modName, byte customID, byte[] bytes)
{
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
new Packets.Mod()
{
// NetHandle = Main.LocalNetHandle,
Target = target,
Name = modName,
CustomPacketID = customID,
Bytes = bytes
}.Pack(outgoingMessage);
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
Client.FlushSendQueue();
#if DEBUG
if (ShowNetworkInfo)
{
@ -193,19 +178,6 @@ namespace RageCoop.Client
}
#endif
}
public static void SendTriggerEvent(string eventName, params object[] args)
{
NetOutgoingMessage outgoingMessage = Client.CreateMessage();
new Packets.ServerClientEvent()
{
EventName = eventName,
Args = new List<object>(args)
}.Pack(outgoingMessage);
Client.SendMessage(outgoingMessage, NetDeliveryMethod.ReliableUnordered, (byte)ConnectionChannel.Event);
Client.FlushSendQueue();
}
#endregion
}
}

View File

@ -30,7 +30,7 @@ namespace RageCoop.Client
if ((Util.GetTickCount64() - Pressed) < 5000 && !Main.MainChat.Focused
#if !NON_INTERACTIVE
&& !Main.MainMenu.MenuPool.AreAnyVisible
&& !Menus.CoopMenu.MenuPool.AreAnyVisible
#endif
)
{
@ -55,7 +55,7 @@ namespace RageCoop.Client
_mainScaleform.CallFunction("SET_DATA_SLOT", i++, $"{player.Latency * 1000:N0}ms", player.Username, 116, 0, i - 1, "", "", 2, "", "", ' ');
}
_mainScaleform.CallFunction("SET_TITLE", "Player list", (Players.Count) + " players");
_mainScaleform.CallFunction("SET_TITLE", "Player list", $"{Players.Count+1} players");
_mainScaleform.CallFunction("DISPLAY_VIEW");
}
public static void SetPlayer(int id, string username,float latency=0)

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyVersion("0.4.0.0")]
[assembly: AssemblyFileVersion("0.4.0.0")]

View File

@ -123,12 +123,19 @@
<ItemGroup>
<Compile Include="DevTools\DevTool.cs" />
<Compile Include="Menus\Sub\DevToolMenu.cs" />
<Compile Include="Misc\WeaponUtil.cs" />
<Compile Include="Menus\Sub\ServersMenu.cs" />
<Compile Include="Scripting\ClientScript.cs" />
<Compile Include="Scripting\Engine.cs" />
<Compile Include="Util\MathExtensions.cs" />
<Compile Include="Util\PedConfigFlags.cs" />
<Compile Include="Util\Util.cs" />
<Compile Include="Util\VehicleExtensions.cs" />
<Compile Include="Util\WeaponUtil.cs" />
<Compile Include="Networking\Chat.cs" />
<Compile Include="COOPAPI.cs" />
<Compile Include="Debug.cs" />
<Compile Include="Networking\DownloadManager.cs" />
<Compile Include="Misc\TaskType.cs" />
<Compile Include="Util\TaskType.cs" />
<Compile Include="Menus\Sub\DebugMenu.cs" />
<Compile Include="Networking\Receive.cs" />
<Compile Include="Networking\Send.cs" />
@ -142,13 +149,13 @@
<Compile Include="Sync\Entities\SyncedVehicle.cs" />
<Compile Include="Main.cs" />
<Compile Include="Networking\MapLoader.cs" />
<Compile Include="Menus\RageCoopMenu.cs" />
<Compile Include="Menus\CoopMenu.cs" />
<Compile Include="Menus\Sub\SettingsMenu.cs" />
<Compile Include="Networking\Networking.cs" />
<Compile Include="PlayerList.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Sync\SyncParameters.cs" />
<Compile Include="Misc\Util.cs" />
<Compile Include="Util\PedExtensions.cs" />
<Compile Include="WorldThread.cs" />
<Compile Include="Settings.cs" />
</ItemGroup>

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client.Scripting
{
/// <summary>
/// Inherit from this class, constructor will be called when the script is loaded.
/// </summary>
public abstract class ClientScript
{
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using System.IO;
using System.CodeDom.Compiler;
namespace RageCoop.Client.Scripting
{
internal class Engine : Core.Scripting.ScriptingEngine
{
public Engine() : base(typeof(ClientScript), Main.Logger)
{
}
}
}

View File

@ -18,7 +18,7 @@ namespace RageCoop.Client
/// <summary>
/// Don't use it!
/// </summary>
public string MasterServer { get; set; } = "https://ragecoop.online/gtav/servers";
public string MasterServer { get; set; } = "[AUTO]";
/// <summary>
/// Don't use it!
/// </summary>
@ -54,7 +54,10 @@ namespace RageCoop.Client
/// The game won't spawn more NPC traffic if the limit is exceeded. -1 for unlimited (not recommended).
/// </summary>
public int WorldVehicleSoftLimit { get; set; } = 35;
/// <summary>
/// Disable automatic respawn.
/// </summary>
public bool DisableAutoRespawn { get; set; } = false;
}
}

View File

@ -8,7 +8,7 @@ using GTA.Math;
namespace RageCoop.Client
{
public abstract class SyncedEntity
internal abstract class SyncedEntity
{
/// <summary>
@ -56,8 +56,13 @@ namespace RageCoop.Client
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public Quaternion Quaternion { get; set; }
public Vector3 Velocity { get; set; }
public abstract void Update();
public void PauseUpdate(ulong frames)
{
LastUpdated=Main.Ticked+frames;
}
}
}

View File

@ -14,7 +14,7 @@ namespace RageCoop.Client
/// <summary>
/// ?
/// </summary>
public partial class SyncedPed:SyncedEntity
internal class SyncedPed:SyncedEntity
{
#region CONSTRUCTORS
@ -30,6 +30,10 @@ namespace RageCoop.Client
MainPed=p;
OwnerID=Main.LocalPlayerID;
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
}
/// <summary>
@ -77,6 +81,7 @@ namespace RageCoop.Client
public Vector3 RotationVelocity { get; set; }
public Vector3 AimCoords { get; set; }
private WeaponAsset WeaponAsset { get; set; }
public override void Update()
@ -246,16 +251,25 @@ namespace RageCoop.Client
Function.Call(Hash.SET_PED_CAN_BE_TARGETTED_BY_PLAYER, MainPed.Handle, Game.Player, true);
Function.Call(Hash.SET_PED_GET_OUT_UPSIDE_DOWN_VEHICLE, MainPed.Handle, false);
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, MainPed.Handle, true, true);
Function.Call(Hash._SET_PED_CAN_PLAY_INJURED_ANIMS, false);
MainPed.BlockPermanentEvents = true;
MainPed.CanWrithe=false;
MainPed.CanBeDraggedOutOfVehicle = true;
MainPed.IsOnlyDamagedByPlayer = false;
MainPed.RelationshipGroup=Main.SyncedPedsGroup;
if (IsPlayer)
{
MainPed.IsInvincible=true;
}
MainPed.IsFireProof=false;
MainPed.IsExplosionProof=false;
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DrownsInWater,false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableMelee, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableExplosionReactions, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_AvoidTearGas, false);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableShockingEvents, true);
MainPed.SetConfigFlag((int)PedConfigFlags.CPED_CONFIG_FLAG_DisableHurt, true);
SetClothes();
if (IsPlayer)
@ -265,7 +279,8 @@ namespace RageCoop.Client
MainPed.AttachedBlip.Color = BlipColor.White;
MainPed.AttachedBlip.Scale = 0.8f;
MainPed.AttachedBlip.Name =Username;
MainPed.IsInvincible=true;
}
// Add to EntityPool so this Character can be accessed by handle.
@ -304,7 +319,6 @@ namespace RageCoop.Client
private int _lastWeaponObj = 0;
#endregion
private bool _isPlayingAnimation = false;
private string[] _currentAnimation = new string[2] { "", "" };
private void DisplayOnFoot()
@ -514,12 +528,6 @@ namespace RageCoop.Client
*/
SmoothTransition();
}
else if (_currentAnimation[1] == "reload_aim")
{
MainPed.Task.ClearAnimation(_currentAnimation[0], _currentAnimation[1]);
_isPlayingAnimation = false;
_currentAnimation = new string[2] { "", "" };
}
else if (IsInCover)
{
@ -569,11 +577,13 @@ namespace RageCoop.Client
}
#region WEAPON
WeaponHash appliedWeaponhash = WeaponHash.Unarmed;
private void CheckCurrentWeapon()
{
if (MainPed.Weapons.Current.Hash != (WeaponHash)CurrentWeaponHash || !WeaponComponents.Compare(_lastWeaponComponents))
{
if (WeaponAsset!=null) { WeaponAsset.MarkAsNoLongerNeeded(); }
WeaponAsset=new WeaponAsset(CurrentWeaponHash);
if (!WeaponAsset.IsLoaded) { WeaponAsset.Request(); }
MainPed.Weapons.RemoveAll();
_lastWeaponObj = Function.Call<int>(Hash.CREATE_WEAPON_OBJECT, CurrentWeaponHash, -1, Position.X, Position.Y, Position.Z, true, 0, 0);
@ -676,7 +686,7 @@ namespace RageCoop.Client
MainPed.PositionNoOffset=Position;
return;
}
var f = dist*(Position+SyncParameters.PositioinPrediction*Velocity-MainPed.Position)+(Velocity-MainPed.Velocity)*0.2f;
var f = dist*(Position+SyncParameters.PositioinPredictionDefault*Velocity-MainPed.Position)+(Velocity-MainPed.Velocity)*0.2f;
if (!localRagdoll) { f*=5; }
if (!(localRagdoll|| MainPed.IsDead))
{

View File

@ -16,7 +16,7 @@ namespace RageCoop.Client
IsMine=true;
MainProjectile = p;
Origin=p.Position;
var shooter = EntityPool.GetPedByHandle(p.Owner.Handle);
var shooter = EntityPool.GetPedByHandle((p.Owner?.Handle).GetValueOrDefault());
if(shooter != null)
{
ShooterID=shooter.ID;
@ -24,14 +24,14 @@ namespace RageCoop.Client
else
{
// Owner will be the vehicle if projectile is shot with a vehicle
var shooterVeh = EntityPool.GetVehicleByHandle(p.Owner.Handle);
var shooterVeh = EntityPool.GetVehicleByHandle((p.Owner?.Handle).GetValueOrDefault());
if (shooterVeh!=null && shooterVeh.MainVehicle.Driver!=null)
{
ShooterID=shooterVeh.MainVehicle.Driver.GetSyncEntity().ID;
}
else
{
Main.Logger.Warning($"Could not find owner for projectile, owner handle:{p.Owner.Handle}");
Main.Logger.Warning($"Could not find owner for projectile:{Hash}");
}
}
@ -45,7 +45,7 @@ namespace RageCoop.Client
public bool Exploded { get; set; } = false;
public Projectile MainProjectile { get; set; }
public int ShooterID { get; set; }
private SyncedPed Shooter { get;set; }
public Vector3 Origin { get; set; }
/// <summary>
@ -75,9 +75,17 @@ namespace RageCoop.Client
{
Asset=new WeaponAsset(Hash);
if (!Asset.IsLoaded) { Asset.Request(); }
World.ShootBullet(Position,Position+Velocity,EntityPool.GetPedByID(ShooterID)?.MainPed,Asset,0);
World.ShootBullet(Position,Position+Velocity,(Shooter=EntityPool.GetPedByID(ShooterID))?.MainPed,Asset,0);
var ps = World.GetAllProjectiles();
MainProjectile=ps[ps.Length-1];
if (Hash==(WeaponHash)VehicleWeaponHash.Tank)
{
var v = Shooter?.MainPed?.CurrentVehicle;
if (v!=null)
{
World.CreateParticleEffectNonLooped(SyncEvents.CorePFXAsset, "muz_tank", v.GetMuzzleInfo().Position, v.Bones[35].ForwardVector.ToEulerRotation(v.Bones[35].UpVector), 1);
}
}
EntityPool.Add(this);
}
}

View File

@ -11,7 +11,7 @@ using RageCoop.Core;
namespace RageCoop.Client
{
public class SyncedVehicle : SyncedEntity
internal class SyncedVehicle : SyncedEntity
{
#region -- CONSTRUCTORS --
@ -52,10 +52,9 @@ namespace RageCoop.Client
#region LAST STATE STORE
private ulong _vehicleStopTime { get; set; }
private byte[] _lastVehicleColors = new byte[] { 0, 0 };
private Dictionary<int, int> _lastVehicleMods = new Dictionary<int, int>();
private byte _lastRadioIndex=255;
#endregion
#region -- CRITICAL STUFF --
@ -63,9 +62,11 @@ namespace RageCoop.Client
public float SteeringAngle { get; set; }
public float ThrottlePower { get; set; }
public float BrakePower { get; set; }
public float DeluxoWingRatio { get; set; } = -1;
#endregion
#region -- VEHICLE STATE --
public VehicleDataFlags Flags { get; set; }
public bool EngineRunning { get; set; }
private bool _lastTransformed = false;
public bool Transformed { get; set; }
@ -88,7 +89,7 @@ namespace RageCoop.Client
/// VehicleSeat,PedID
/// </summary>
public Dictionary<VehicleSeat, SyncedPed> Passengers { get; set; }
public byte RadioStation = 255;
private long _lastPositionCalibrated { get; set; }
#endregion
@ -99,10 +100,8 @@ namespace RageCoop.Client
// Check if all data avalible
if(!IsReady) { return; }
// Skip update if no new sync message has arrived.
if (!NeedUpdate) { return; }
#endregion
#region -- CHECK EXISTENCE --
if ((MainVehicle == null) || (!MainVehicle.Exists()) || (MainVehicle.Model.Hash != ModelHash))
@ -129,27 +128,22 @@ namespace RageCoop.Client
}
if (MainVehicle.Position.DistanceTo(Position)<5)
{
MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPrediction - MainVehicle.Position);
_lastPositionCalibrated=Main.Counter.ElapsedMilliseconds;
MainVehicle.Velocity = Velocity+5*(Position+Velocity*SyncParameters.PositioinPredictionDefault - MainVehicle.Position);
MainVehicle.Quaternion=Quaternion.Slerp(MainVehicle.Quaternion, Quaternion, 0.35f);
}
else
{
MainVehicle.Position=Position;
MainVehicle.Velocity=Velocity;
MainVehicle.Quaternion=Quaternion;
}
Vector3 r = GetCalibrationRotation();
if (r.Length() < 20f)
// Vector3 r = GetCalibrationRotation();
MainVehicle.RotationVelocity = RotationVelocity;
if (DeluxoWingRatio!=-1)
{
MainVehicle.RotationVelocity = r * 0.15f + RotationVelocity;
MainVehicle.SetDeluxoWingRatio(DeluxoWingRatio);
}
else
{
MainVehicle.Rotation = Rotation;
MainVehicle.RotationVelocity = RotationVelocity;
}
_vehicleStopTime = Util.GetTickCount64();
#endregion
if (LastStateSynced>LastUpdated)
{
#region -- SYNC STATE --
@ -168,7 +162,11 @@ namespace RageCoop.Client
{
SyncedPed c = Passengers[seat];
if ((c!=null)&&c.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)) {
if (c?.ID==Main.LocalPlayerID && (RadioStation!=_lastRadioIndex))
{
Util.SetPlayerRadioIndex(RadioStation);
}
if (c?.MainPed!=null&&(!currentPassengers.ContainsKey(i))&&(!c.MainPed.IsBeingJacked)&&(!c.MainPed.IsTaskActive(TaskType.CTaskExitVehicleSeat))) {
Passengers[seat].MainPed.SetIntoVehicle(MainVehicle, seat);
}
}
@ -303,7 +301,20 @@ namespace RageCoop.Client
}
MainVehicle.LockStatus=LockStatus;
if (Flags.HasFlag(VehicleDataFlags.IsDeluxoHovering))
{
if (!MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(true);
}
}
else if(ModelHash==1483171323)
{
if (MainVehicle.IsDeluxoHovering())
{
MainVehicle.SetDeluxoHoverState(false);
}
}
#endregion
}
@ -311,6 +322,8 @@ namespace RageCoop.Client
}
private Vector3 GetCalibrationRotation()
{
return (Quaternion-MainVehicle.Quaternion).ToEulerAngles().ToDegree();
/*
var r = Rotation-MainVehicle.Rotation;
if (r.X>180) { r.X=r.X-360; }
else if(r.X<-180) { r.X=360+r.X; }
@ -321,6 +334,7 @@ namespace RageCoop.Client
if (r.Z>180) { r.Z=r.Z-360; }
else if (r.Z<-180) { r.Z=360+r.Z; }
return r;
*/
}
private void CreateVehicle()
{
@ -336,7 +350,7 @@ namespace RageCoop.Client
{
EntityPool.Add( this);
}
MainVehicle.Rotation = Rotation;
MainVehicle.Quaternion = Quaternion;
if (MainVehicle.HasRoof)
{
@ -385,7 +399,9 @@ namespace RageCoop.Client
}
#endregion
#region OUTGOING
public float LastNozzleAngle { get; set; }
#endregion
}
}

View File

@ -388,6 +388,7 @@ namespace RageCoop.Client
if (Main.Ticked%20==0)
{
Networking.SendPed(c);
Networking.SendPedState(c);
}
else
@ -449,10 +450,11 @@ namespace RageCoop.Client
// Outgoing sync
if (v.IsMine)
{
SyncEvents.Check(v);
if (Main.Ticked%20==0)
{
Networking.SendVehicle(v);
Networking.SendVehicleState(v);
}
else
{

View File

@ -12,7 +12,6 @@ using System.Threading;
namespace RageCoop.Client {
internal static class SyncEvents
{
#region TRIGGER
public static void TriggerPedKilled(SyncedPed victim)
{
@ -34,9 +33,9 @@ namespace RageCoop.Client {
{
if (seat==VehicleSeat.Driver)
{
TriggerChangeOwner(veh, c.ID);
veh.OwnerID=Main.LocalPlayerID;
veh.LastSynced=Main.Ticked;
TriggerChangeOwner(veh, c.ID);
}
Networking.Send(new Packets.EnteredVehicle()
{
@ -61,13 +60,6 @@ namespace RageCoop.Client {
{
Main.Logger.Trace($"bullet shot:{(WeaponHash)hash}");
// Minigun, not working for some reason
if (hash==(uint)WeaponHash.Minigun)
{
hash=(uint)WeaponHash.HeavyRifle;
}
// Valkyire, not working for some reason
if (hash==2756787765) { hash=(uint)WeaponHash.HeavyRifle; }
var start = owner.MainPed.GetMuzzlePosition();
if (owner.MainPed.IsOnTurretSeat()) { start=owner.MainPed.Bones[Bone.SkelHead].Position; }
@ -89,10 +81,6 @@ namespace RageCoop.Client {
public static void TriggerVehBulletShot(uint hash, Vehicle veh, SyncedPed owner)
{
if (hash==(uint)VehicleWeaponHash.PlayerBuzzard)
{
hash=(uint)WeaponHash.HeavyRifle;
}
// ANNIHL
if (veh.Model.Hash==837858166)
{
@ -107,19 +95,29 @@ namespace RageCoop.Client {
if (info==null) { Main.Logger.Warning($"Failed to get muzzle info for vehicle:{veh.DisplayName}");return; }
Networking.SendBulletShot(info.Position,info.Position+info.ForawardVector,hash,owner.ID);
}
public static void TriggerNozzleTransform(int vehID,bool hover)
{
Networking.Send(new Packets.NozzleTransform() { VehicleID=vehID, Hover=hover }, ConnectionChannel.SyncEvents);
}
#endregion
#region HANDLE
public static ParticleEffectAsset CorePFXAsset = new ParticleEffectAsset("core");
static WeaponAsset _weaponAsset = default;
static uint _lastWeaponHash;
private static void HandleLeaveVehicle(Packets.LeaveVehicle p)
{
var ped = EntityPool.GetPedByID(p.ID)?.MainPed;
var ped = EntityPool.GetPedByID(p.ID);
var flag = LeaveVehicleFlags.None;
if (ped.MainPed?.CurrentVehicle==null) { return; }
// Bail out
if (ped?.CurrentVehicle==null) { return; }
if (ped.CurrentVehicle.Speed>5) { flag|=LeaveVehicleFlags.BailOut;}
ped.Task.LeaveVehicle(flag) ;
if (ped.MainPed.CurrentVehicle.Speed>5) { flag|=LeaveVehicleFlags.BailOut;}
ped.PauseUpdate((ulong)Game.FPS*2);
ped.MainPed.Task.LeaveVehicle(flag) ;
}
private static void HandlePedKilled(Packets.PedKilled p)
{
@ -150,30 +148,50 @@ namespace RageCoop.Client {
v.OwnerID=p.NewOwnerID;
v.ModelHash=v.MainVehicle.Model;
v.LastSynced=Main.Ticked;
// So this vehicle doesn's get re-spawned
}
private static ParticleEffectAsset CorePFXAsset = default;
static WeaponAsset _weaponAsset = default;
static uint _lastWeaponHash;
private static void HandleNozzleTransform(Packets.NozzleTransform p)
{
EntityPool.GetVehicleByID(p.VehicleID)?.MainVehicle?.SetNozzleAngel(p.Hover ? 1 : 0);
}
private static void HandleBulletShot(Vector3 start, Vector3 end, uint weaponHash, int ownerID)
{
if (CorePFXAsset==default) {
CorePFXAsset= new ParticleEffectAsset("core");
switch (weaponHash)
{
// Minigun, not working for some reason
case (uint)WeaponHash.Minigun:
weaponHash=1176362416;
break;
// Valkyire, not working for some reason
case 2756787765:
weaponHash=1176362416;
break;
// SAVAGE
case 1638077257:
weaponHash=(uint)VehicleWeaponHash.PlayerLazer;
break;
case (uint)VehicleWeaponHash.PlayerBuzzard:
weaponHash=1176362416;
break ;
}
if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); }
var p = EntityPool.GetPedByID(ownerID)?.MainPed;
if (p == null) { p=Game.Player.Character; Main.Logger.Warning("Failed to find owner for bullet"); }
if (!CorePFXAsset.IsLoaded) { CorePFXAsset.Request(); }
if (_lastWeaponHash!=weaponHash)
{
_weaponAsset.MarkAsNoLongerNeeded();
_weaponAsset=new WeaponAsset(weaponHash);
_lastWeaponHash=weaponHash;
}
if (!_weaponAsset.IsLoaded) { _weaponAsset.Request(); }
World.ShootBullet(start, end, p, _weaponAsset, p.GetWeaponDamage());
var w = p.Weapons.CurrentWeaponObject;
if(w != null)
World.ShootBullet(start, end, p, _weaponAsset, p.GetWeaponDamage(weaponHash));
Prop w;
if(((w = p.Weapons.CurrentWeaponObject) != null)&&(p.VehicleWeapon==VehicleWeaponHash.Invalid))
{
if (p.Weapons.Current.Components.GetSuppressorComponent().Active)
{
@ -185,6 +203,13 @@ namespace RageCoop.Client {
}
}
else if (p.VehicleWeapon!=VehicleWeaponHash.Invalid)
{
if (p.VehicleWeapon==VehicleWeaponHash.Tank)
{
World.CreateParticleEffectNonLooped(CorePFXAsset, "muz_tank", p.CurrentVehicle.GetMuzzleInfo().Position, p.CurrentVehicle.Bones[35].ForwardVector.ToEulerRotation(p.CurrentVehicle.Bones[35].UpVector), 1);
}
}
}
public static void HandleEvent(PacketTypes type,byte[] data)
{
@ -194,7 +219,7 @@ namespace RageCoop.Client {
{
Packets.BulletShot p = new Packets.BulletShot();
p.Unpack(data);
HandleBulletShot(p.StartPosition.ToVector(), p.EndPosition.ToVector(), p.WeaponHash, p.OwnerID);
HandleBulletShot(p.StartPosition, p.EndPosition, p.WeaponHash, p.OwnerID);
break;
}
case PacketTypes.EnteringVehicle:
@ -234,13 +259,22 @@ namespace RageCoop.Client {
HandleEnteredVehicle(packet.PedID,packet.VehicleID,(VehicleSeat)packet.VehicleSeat);
break;
}
case PacketTypes.NozzleTransform:
{
var packet = new Packets.NozzleTransform();
packet.Unpack(data);
HandleNozzleTransform(packet);
break;
}
}
}
public static int GetWeaponDamage(this Ped p)
public static int GetWeaponDamage(this Ped p,uint hash)
{
if (p.VehicleWeapon!=VehicleWeaponHash.Invalid)
if(p.IsInVehicle() && (hash!=(uint)p.Weapons.Current.Hash))
{
return 30;
// This is a vehicle weapon
p.VehicleWeapon=(VehicleWeaponHash)hash;
return 100;
}
switch (p.Weapons.Current.Group)
{
@ -264,6 +298,8 @@ namespace RageCoop.Client {
public static void Check(SyncedPed c)
{
Ped subject = c.MainPed;
// Check bullets
if (subject.IsShooting)
{
if (!subject.IsUsingProjectileWeapon())
@ -360,7 +396,21 @@ namespace RageCoop.Client {
c._lastEnteringVehicle=g;
}
public static void Check(SyncedVehicle v)
{
if (v.MainVehicle!=null&&v.MainVehicle.HasNozzle())
{
if((v.LastNozzleAngle==1) && (v.MainVehicle.GetNozzleAngel()!=1))
{
TriggerNozzleTransform(v.ID,false);
}
else if((v.LastNozzleAngle==0) && (v.MainVehicle.GetNozzleAngel()!=0))
{
TriggerNozzleTransform(v.ID,true);
}
v.LastNozzleAngle=v.MainVehicle.GetNozzleAngel();
}
}
#endregion
}
}

View File

@ -8,6 +8,6 @@ namespace RageCoop.Client
{
internal class SyncParameters
{
public static float PositioinPrediction = 0.03f;
public static float PositioinPredictionDefault = 0.01f;
}
}

View File

@ -0,0 +1,149 @@
using System;
using GTA.Math;
namespace RageCoop.Client
{
internal static class MathExtensions
{
/// <summary>
///
/// </summary>
public static Vector3 ToVector(this Quaternion vec)
{
return new Vector3()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z
};
}
/// <summary>
///
/// </summary>
public static Quaternion ToQuaternion(this Vector3 vec, float vW = 0.0f)
{
return new Quaternion()
{
X = vec.X,
Y = vec.Y,
Z = vec.Z,
W = vW
};
}
public static float Denormalize(this float h)
{
return h < 0f ? h + 360f : h;
}
public static float ToRadians(this float val)
{
return (float)(Math.PI / 180) * val;
}
public static Vector3 ToRadians(this Vector3 i)
{
return new Vector3()
{
X = ToRadians(i.X),
Y = ToRadians(i.Y),
Z = ToRadians(i.Z),
};
}
public static Quaternion ToQuaternion(this Vector3 vect)
{
vect = new Vector3()
{
X = vect.X.Denormalize() * -1,
Y = vect.Y.Denormalize() - 180f,
Z = vect.Z.Denormalize() - 180f,
};
vect = vect.ToRadians();
float rollOver2 = vect.Z * 0.5f;
float sinRollOver2 = (float)Math.Sin((double)rollOver2);
float cosRollOver2 = (float)Math.Cos((double)rollOver2);
float pitchOver2 = vect.Y * 0.5f;
float sinPitchOver2 = (float)Math.Sin((double)pitchOver2);
float cosPitchOver2 = (float)Math.Cos((double)pitchOver2);
float yawOver2 = vect.X * 0.5f; // pitch
float sinYawOver2 = (float)Math.Sin((double)yawOver2);
float cosYawOver2 = (float)Math.Cos((double)yawOver2);
Quaternion result = new Quaternion()
{
X = cosYawOver2 * cosPitchOver2 * cosRollOver2 + sinYawOver2 * sinPitchOver2 * sinRollOver2,
Y = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2,
Z = cosYawOver2 * sinPitchOver2 * cosRollOver2 + sinYawOver2 * cosPitchOver2 * sinRollOver2,
W = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2
};
return result;
}
public static double DegToRad(double deg)
{
return deg * Math.PI / 180.0;
}
public static Vector3 ToEulerRotation(this Vector3 dir, Vector3 up)
{
var rot = Quaternion.LookRotation(dir.Normalized, up).ToEulerAngles().ToDegree();
return rot;
}
public static Vector3 ToDegree(this Vector3 radian)
{
return radian*(float)(180/Math.PI);
}
public static Vector3 ToEulerAngles(this Quaternion q)
{
Vector3 angles = new Vector3();
// roll / x
double sinr_cosp = 2 * (q.W * q.X + q.Y * q.Z);
double cosr_cosp = 1 - 2 * (q.X * q.X + q.Y * q.Y);
angles.X = (float)Math.Atan2(sinr_cosp, cosr_cosp);
// pitch / y
double sinp = 2 * (q.W * q.Y - q.Z * q.X);
if (Math.Abs(sinp) >= 1)
{
angles.Y = CopySign(Math.PI / 2, sinp);
}
else
{
angles.Y = (float)Math.Asin(sinp);
}
// yaw / z
double siny_cosp = 2 * (q.W * q.Z + q.X * q.Y);
double cosy_cosp = 1 - 2 * (q.Y * q.Y + q.Z * q.Z);
angles.Z = (float)Math.Atan2(siny_cosp, cosy_cosp);
return angles;
}
private static float CopySign(double x, double y)
{
bool isPositive = y>=0;
if (isPositive)
{
if (x>=0) { return (float)x; } else { return (float)-x; }
}
else
{
if (x>=0) { return (float)-x; } else { return (float)x; }
}
}
public static double AngelTo(this Vector3 v1, Vector3 v2)
{
return Math.Acos(v1.GetCosTheta(v2));
}
public static float GetCosTheta(this Vector3 v1, Vector3 v2)
{
return Vector3.Dot(v1, v2)/(v1.Length()*v2.Length());
}
}
}

View File

@ -0,0 +1,471 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RageCoop.Client
{
// Potential names and hash collisions included as comments
public enum PedConfigFlags
{
_0x67D1A445 = 0,
_0xC63DE95E = 1,
CPED_CONFIG_FLAG_NoCriticalHits = 2,
CPED_CONFIG_FLAG_DrownsInWater = 3,
CPED_CONFIG_FLAG_DisableReticuleFixedLockon = 4,
_0x37D196F4 = 5,
_0xE2462399 = 6,
CPED_CONFIG_FLAG_UpperBodyDamageAnimsOnly = 7,
_0xEDDEB838 = 8,
_0xB398B6FD = 9,
_0xF6664E68 = 10,
_0xA05E7CA3 = 11,
_0xCE394045 = 12,
CPED_CONFIG_FLAG_NeverLeavesGroup = 13,
_0xCD8D1411 = 14,
_0xB031F1A9 = 15,
_0xFE65BEE3 = 16,
CPED_CONFIG_FLAG_BlockNonTemporaryEvents = 17,
_0x380165BD = 18,
_0x07C045C7 = 19,
_0x583B5E2D = 20,
_0x475EDA58 = 21,
_0x8629D05B = 22,
_0x1522968B = 23,
CPED_CONFIG_FLAG_IgnoreSeenMelee = 24,
_0x4CC09C4B = 25,
_0x034F3053 = 26,
_0xD91BA7CC = 27,
_0x5C8DC66E = 28,
_0x8902EAA0 = 29,
_0x6580B9D2 = 30,
_0x0EF7A297 = 31,
_0x6BF86E5B = 32,
CPED_CONFIG_FLAG_DieWhenRagdoll = 33,
CPED_CONFIG_FLAG_HasHelmet = 34,
CPED_CONFIG_FLAG_UseHelmet = 35,
_0xEEB3D630 = 36,
_0xB130D17B = 37,
_0x5F071200 = 38,
CPED_CONFIG_FLAG_DisableEvasiveDives = 39,
_0xC287AAFF = 40,
_0x203328CC = 41,
CPED_CONFIG_FLAG_DontInfluenceWantedLevel = 42,
CPED_CONFIG_FLAG_DisablePlayerLockon = 43,
CPED_CONFIG_FLAG_DisableLockonToRandomPeds = 44,
_0xEC4A8ACF = 45,
_0xDB115BFA = 46,
CPED_CONFIG_FLAG_PedBeingDeleted = 47,
CPED_CONFIG_FLAG_BlockWeaponSwitching = 48,
_0xF8E99565 = 49,
_0xDD17FEE6 = 50,
_0x7ED9B2C9 = 51,
_0x655E8618 = 52,
_0x5A6C1F6E = 53,
_0xD749FC41 = 54,
_0x357F63F3 = 55,
_0xC5E60961 = 56,
_0x29275C3E = 57,
CPED_CONFIG_FLAG_IsFiring = 58,
CPED_CONFIG_FLAG_WasFiring = 59,
CPED_CONFIG_FLAG_IsStanding = 60,
CPED_CONFIG_FLAG_WasStanding = 61,
CPED_CONFIG_FLAG_InVehicle = 62,
CPED_CONFIG_FLAG_OnMount = 63,
CPED_CONFIG_FLAG_AttachedToVehicle = 64,
CPED_CONFIG_FLAG_IsSwimming = 65,
CPED_CONFIG_FLAG_WasSwimming = 66,
CPED_CONFIG_FLAG_IsSkiing = 67,
CPED_CONFIG_FLAG_IsSitting = 68,
CPED_CONFIG_FLAG_KilledByStealth = 69,
CPED_CONFIG_FLAG_KilledByTakedown = 70,
CPED_CONFIG_FLAG_Knockedout = 71,
_0x3E3C4560 = 72,
_0x2994C7B7 = 73,
_0x6D59D275 = 74,
CPED_CONFIG_FLAG_UsingCoverPoint = 75,
CPED_CONFIG_FLAG_IsInTheAir = 76,
_0x2D493FB7 = 77,
CPED_CONFIG_FLAG_IsAimingGun = 78,
_0x14D69875 = 79,
_0x40B05311 = 80,
_0x8B230BC5 = 81,
_0xC74E5842 = 82,
_0x9EA86147 = 83,
_0x674C746C = 84,
_0x3E56A8C2 = 85,
_0xC144A1EF = 86,
_0x0548512D = 87,
_0x31C93909 = 88,
_0xA0269315 = 89,
_0xD4D59D4D = 90,
_0x411D4420 = 91,
_0xDF4AEF0D = 92,
CPED_CONFIG_FLAG_ForcePedLoadCover = 93,
_0x300E4CD3 = 94,
_0xF1C5BF04 = 95,
_0x89C2EF13 = 96,
CPED_CONFIG_FLAG_VaultFromCover = 97,
_0x02A852C8 = 98,
_0x3D9407F1 = 99,
_0x319B4558 = 100,
CPED_CONFIG_FLAG_ForcedAim = 101,
_0xB942D71A = 102,
_0xD26C55A8 = 103,
_0xB89E703B = 104,
CPED_CONFIG_FLAG_ForceReload = 105,
_0xD9E73DA2 = 106,
_0xFF71DC2C = 107,
_0x1E27E8D8 = 108,
_0xF2C53966 = 109,
_0xC4DBE247 = 110,
_0x83C0A4BF = 111,
_0x0E0FAF8C = 112,
_0x26616660 = 113,
_0x43B80B79 = 114,
_0x0D2A9309 = 115,
_0x12C1C983 = 116,
CPED_CONFIG_FLAG_BumpedByPlayer = 117,
_0xE586D504 = 118,
_0x52374204 = 119,
CPED_CONFIG_FLAG_IsHandCuffed = 120,
CPED_CONFIG_FLAG_IsAnkleCuffed = 121,
CPED_CONFIG_FLAG_DisableMelee = 122,
_0xFE714397 = 123,
_0xB3E660BD = 124,
_0x5FED6BFD = 125,
_0xC9D6F66F = 126,
_0x519BC986 = 127,
CPED_CONFIG_FLAG_CanBeAgitated = 128,
_0x9A4B617C = 129, // CPED_CONFIG_FLAG_FaceDirInsult
_0xDAB70E9F = 130,
_0xE569438A = 131,
_0xBBC77D6D = 132,
_0xCB59EF0F = 133,
_0x8C5EA971 = 134,
CPED_CONFIG_FLAG_IsScuba = 135,
CPED_CONFIG_FLAG_WillArrestRatherThanJack = 136,
_0xDCE59B58 = 137,
CPED_CONFIG_FLAG_RidingTrain = 138,
CPED_CONFIG_FLAG_ArrestResult = 139,
CPED_CONFIG_FLAG_CanAttackFriendly = 140,
_0x98A4BE43 = 141,
_0x6901E731 = 142,
_0x9EC9BF6C = 143,
_0x42841A8F = 144,
CPED_CONFIG_FLAG_ShootingAnimFlag = 145,
CPED_CONFIG_FLAG_DisableLadderClimbing = 146,
CPED_CONFIG_FLAG_StairsDetected = 147,
CPED_CONFIG_FLAG_SlopeDetected = 148,
_0x1A15670B = 149,
_0x61786EE5 = 150,
_0xCB9186BD = 151,
_0xF0710152 = 152,
_0x43DFE310 = 153,
_0xC43C624E = 154,
CPED_CONFIG_FLAG_CanPerformArrest = 155,
CPED_CONFIG_FLAG_CanPerformUncuff = 156,
CPED_CONFIG_FLAG_CanBeArrested = 157,
_0xF7960FF5 = 158,
_0x59564113 = 159,
_0x0C6C3099 = 160,
_0x645F927A = 161,
_0xA86549B9 = 162,
_0x8AAF337A = 163,
_0x13BAA6E7 = 164,
_0x5FB9D1F5 = 165,
CPED_CONFIG_FLAG_IsInjured = 166,
_0x6398A20B = 167,
_0xD8072639 = 168,
_0xA05B1845 = 169,
_0x83F6D220 = 170,
_0xD8430331 = 171,
_0x4B547520 = 172,
_0xE66E1406 = 173,
_0x1C4BFE0C = 174,
_0x90008BFA = 175,
_0x07C7A910 = 176,
_0xF15F8191 = 177,
_0xCE4E8BE2 = 178,
_0x1D46E4F2 = 179,
CPED_CONFIG_FLAG_IsInCustody = 180,
_0xE4FD9B3A = 181,
_0x67AE0812 = 182,
CPED_CONFIG_FLAG_IsAgitated = 183,
CPED_CONFIG_FLAG_PreventAutoShuffleToDriversSeat = 184,
_0x7B2D325E = 185,
CPED_CONFIG_FLAG_EnableWeaponBlocking = 186,
CPED_CONFIG_FLAG_HasHurtStarted = 187,
CPED_CONFIG_FLAG_DisableHurt = 188,
CPED_CONFIG_FLAG_PlayerIsWeird = 189,
_0x32FC208B = 190,
_0x0C296E5A = 191,
_0xE63B73EC = 192,
_0x04E9CC80 = 193,
CPED_CONFIG_FLAG_UsingScenario = 194,
CPED_CONFIG_FLAG_VisibleOnScreen = 195,
_0xD88C58A1 = 196,
_0x5A3DCF43 = 197, // CPED_CONFIG_FLAG_AvoidUnderSide
_0xEA02B420 = 198,
_0x3F559CFF = 199,
_0x8C55D029 = 200,
_0x5E6466F6 = 201,
_0xEB5AD706 = 202,
_0x0EDDDDE7 = 203,
_0xA64F7B1D = 204,
_0x48532CBA = 205,
_0xAA25A9E7 = 206,
_0x415B26B9 = 207,
CPED_CONFIG_FLAG_DisableExplosionReactions = 208,
CPED_CONFIG_FLAG_DodgedPlayer = 209,
_0x67405504 = 210,
_0x75DDD68C = 211,
_0x2AD879B4 = 212,
_0x51486F91 = 213,
_0x32F79E21 = 214,
_0xBF099213 = 215,
_0x054AC8E2 = 216,
_0x14E495CC = 217,
_0x3C7DF9DF = 218,
_0x848FFEF2 = 219,
CPED_CONFIG_FLAG_DontEnterLeadersVehicle = 220,
_0x2618E1CF = 221,
_0x84F722FA = 222,
_0xD1B87B1F = 223,
_0x728AA918 = 224,
CPED_CONFIG_FLAG_DisablePotentialToBeWalkedIntoResponse = 225,
CPED_CONFIG_FLAG_DisablePedAvoidance = 226,
_0x59E91185 = 227,
_0x1EA7225F = 228,
CPED_CONFIG_FLAG_DisablePanicInVehicle = 229,
_0x6DCA7D88 = 230,
_0xFC3E572D = 231,
_0x08E9F9CF = 232,
_0x2D3BA52D = 233,
_0xFD2F53EA = 234,
_0x31A1B03B = 235,
CPED_CONFIG_FLAG_IsHoldingProp = 236,
_0x82ED0A66 = 237, // CPED_CONFIG_FLAG_BlocksPathingWhenDead
_0xCE57C9A3 = 238,
_0x26149198 = 239,
_0x1B33B598 = 240,
_0x719B6E87 = 241,
_0x13E8E8E8 = 242,
_0xF29739AE = 243,
_0xABEA8A74 = 244,
_0xB60EA2BA = 245,
_0x536B0950 = 246,
_0x0C754ACA = 247,
CPED_CONFIG_FLAG_DisableVehicleSeatRandomAnimations = 248,
_0x12659168 = 249,
_0x1BDF2F04 = 250,
_0x7728FAA3 = 251,
_0x6A807ED8 = 252,
CPED_CONFIG_FLAG_OnStairs = 253,
_0xE1A2F73F = 254,
_0x5B3697C8 = 255,
_0xF1EB20A9 = 256,
_0x8B7DF407 = 257,
_0x329DCF1A = 258,
_0x8D90DD1B = 259,
_0xB8A292B7 = 260,
_0x8374B087 = 261,
_0x2AF558F0 = 262,
_0x82251455 = 263,
_0x30CF498B = 264,
_0xE1CD50AF = 265,
_0x72E4AE48 = 266,
_0xC2657EA1 = 267,
_0x29FF6030 = 268,
_0x8248A5EC = 269,
CPED_CONFIG_FLAG_OnStairSlope = 270,
_0xA0897933 = 271,
CPED_CONFIG_FLAG_DontBlipCop = 272,
CPED_CONFIG_FLAG_ClimbedShiftedFence = 273,
_0xF7823618 = 274,
_0xDC305CCE = 275, // CPED_CONFIG_FLAG_KillWhenTrapped
CPED_CONFIG_FLAG_EdgeDetected = 276,
_0x92B67896 = 277,
_0xCAD677C9 = 278,
CPED_CONFIG_FLAG_AvoidTearGas = 279,
_0x5276AC7B = 280,
_0x1032692A = 281,
_0xDA23E7F1 = 282,
_0x9139724D = 283,
_0xA1457461 = 284,
_0x4186E095 = 285,
_0xAC68E2EB = 286,
CPED_CONFIG_FLAG_RagdollingOnBoat = 287,
CPED_CONFIG_FLAG_HasBrandishedWeapon = 288,
_0x1B9EE8A1 = 289,
_0xF3F5758C = 290,
_0x2A9307F1 = 291,
_0x7403D216 = 292,
_0xA06A3C6C = 293,
CPED_CONFIG_FLAG_DisableShockingEvents = 294,
_0xF8DA25A5 = 295,
_0x7EF55802 = 296,
_0xB31F1187 = 297,
_0x84315402 = 298,
_0x0FD69867 = 299,
_0xC7829B67 = 300,
CPED_CONFIG_FLAG_DisablePedConstraints = 301,
_0x6D23CF25 = 302,
_0x2ADA871B = 303,
_0x47BC8A58 = 304,
_0xEB692FA5 = 305,
_0x4A133C50 = 306,
_0xC58099C3 = 307,
_0xF3D76D41 = 308,
_0xB0EEE9F2 = 309,
CPED_CONFIG_FLAG_IsInCluster = 310,
_0x0FA153EF = 311,
_0xD73F5CD3 = 312,
_0xD4136C22 = 313,
_0xE404CA6B = 314,
_0xB9597446 = 315,
_0xD5C98277 = 316,
_0xD5060A9C = 317,
_0x3E5F1CBB = 318,
_0xD8BE1D54 = 319,
_0x0B1F191F = 320,
_0xC995167A = 321,
CPED_CONFIG_FLAG_HasHighHeels = 322,
_0x86B01E54 = 323,
_0x3A56FE15 = 324,
_0xC03B736C = 325, // CPED_CONFIG_FLAG_SpawnedAtScenario
_0xBBF47729 = 326,
_0x22B668A8 = 327,
_0x2624D4D4 = 328,
CPED_CONFIG_FLAG_DisableTalkTo = 329,
CPED_CONFIG_FLAG_DontBlip = 330,
CPED_CONFIG_FLAG_IsSwitchingWeapon = 331,
_0x630F55F3 = 332,
_0x150468FD = 333,
_0x914EBD6B = 334,
_0x79AF3B6D = 335,
_0x75C7A632 = 336,
_0x52D530E2 = 337,
_0xDB2A90E0 = 338,
_0x5922763D = 339,
_0x12ADB567 = 340,
_0x105C8518 = 341,
_0x106F703D = 342,
_0xED152C3E = 343,
_0xA0EFE6A8 = 344,
_0xBF348C82 = 345,
_0xCDDFE830 = 346,
_0x7B59BD9B = 347,
_0x0124C788 = 348,
CPED_CONFIG_FLAG_EquipJetpack = 349,
_0x08D361A5 = 350,
_0xE13D1F7C = 351,
_0x40E25FB9 = 352,
_0x930629D9 = 353,
_0xECCF0C7F = 354,
_0xB6E9613B = 355,
_0x490C0478 = 356,
_0xE8865BEA = 357,
_0xF3C34A29 = 358,
CPED_CONFIG_FLAG_IsDuckingInVehicle = 359,
_0xF660E115 = 360,
_0xAB0E6DED = 361,
CPED_CONFIG_FLAG_HasReserveParachute = 362,
CPED_CONFIG_FLAG_UseReserveParachute = 363,
_0x5C5D9CD3 = 364,
_0x8F7701F3 = 365,
_0xBC4436AD = 366,
_0xD7E07D37 = 367,
_0x03C4FD24 = 368,
_0x7675789A = 369,
_0xB7288A88 = 370,
_0xC06B6291 = 371,
_0x95A4A805 = 372,
_0xA8E9A042 = 373,
CPED_CONFIG_FLAG_NeverLeaveTrain = 374,
_0xBAC674B3 = 375,
_0x147F1FFB = 376,
_0x4376DD79 = 377,
_0xCD3DB518 = 378,
_0xFE4BA4B6 = 379,
_0x5DF03A55 = 380,
_0xBCD816CD = 381,
_0xCF02DD69 = 382,
_0xF73AFA2E = 383,
_0x80B9A9D0 = 384,
_0xF601F7EE = 385,
_0xA91350FC = 386,
_0x3AB23B96 = 387,
CPED_CONFIG_FLAG_IsClimbingLadder = 388,
CPED_CONFIG_FLAG_HasBareFeet = 389,
_0xB4B1CD4C = 390,
_0x5459AFB8 = 391,
_0x54F27667 = 392,
_0xC11D3E8F = 393,
_0x5419EB3E = 394,
_0x82D8DBB4 = 395,
_0x33B02D2F = 396,
_0xAE66176D = 397,
_0xA2692593 = 398,
_0x714C7E31 = 399,
_0xEC488AC7 = 400,
_0xAE398504 = 401,
_0xABC58D72 = 402,
_0x5E5B9591 = 403,
_0x6BA1091E = 404,
_0x77840177 = 405,
_0x1C7ACAC4 = 406,
_0x124420E9 = 407,
_0x75A65587 = 408,
_0xDFD2D55B = 409,
_0xBDD39919 = 410,
_0x43DEC267 = 411,
_0xE42B7797 = 412,
CPED_CONFIG_FLAG_IsHolsteringWeapon = 413,
_0x4F8149F5 = 414,
_0xDD9ECA7A = 415,
_0x9E7EF9D2 = 416,
_0x2C6ED942 = 417,
CPED_CONFIG_FLAG_IsSwitchingHelmetVisor = 418,
_0xA488727D = 419,
_0xCFF5F6DE = 420,
_0x6D614599 = 421,
CPED_CONFIG_FLAG_DisableVehicleCombat = 422,
_0xFE401D26 = 423,
CPED_CONFIG_FLAG_FallsLikeAircraft = 424,
_0x2B42AE82 = 425,
_0x7A95734F = 426,
_0xDF4D8617 = 427,
_0x578F1F14 = 428,
CPED_CONFIG_FLAG_DisableStartEngine = 429,
CPED_CONFIG_FLAG_IgnoreBeingOnFire = 430,
_0x153C9500 = 431,
_0xCB7A632E = 432,
_0xDE727981 = 433,
CPED_CONFIG_FLAG_DisableHomingMissileLockon = 434,
_0x12BBB935 = 435,
_0xAD0A1277 = 436,
_0xEA6AA46A = 437,
CPED_CONFIG_FLAG_DisableHelmetArmor = 438,
_0xCB7F3A1E = 439,
_0x50178878 = 440,
_0x051B4F0D = 441,
_0x2FC3DECC = 442,
_0xC0030B0B = 443,
_0xBBDAF1E9 = 444,
_0x944FE59C = 445,
_0x506FBA39 = 446,
_0xDD45FE84 = 447,
_0xE698AE75 = 448,
_0x199633F8 = 449,
CPED_CONFIG_FLAG_PedIsArresting = 450,
CPED_CONFIG_FLAG_IsDecoyPed = 451,
_0x3A251D83 = 452,
_0xA56F6986 = 453,
_0x1D19C622 = 454,
_0xB68D3EAB = 455,
CPED_CONFIG_FLAG_CanBeIncapacitated = 456,
_0x4BD5EBAD = 457,
}
}

View File

@ -0,0 +1,390 @@
using System;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using RageCoop.Core;
using GTA;
using GTA.Native;
using GTA.Math;
using System.Linq;
using System.Diagnostics;
namespace RageCoop.Client
{
internal static partial class PedExtensions
{
public static bool IsBetween<T>(this T item, T start, T end)
{
return Comparer<T>.Default.Compare(item, start) >= 0 && Comparer<T>.Default.Compare(item, end) <= 0;
}
public static bool Compare<T, Y>(this Dictionary<T, Y> item, Dictionary<T, Y> item2)
{
if (item == null || item2 == null || item.Count != item2.Count)
{
return false;
}
foreach (KeyValuePair<T, Y> pair in item)
{
if (item2.TryGetValue(pair.Key, out Y value) && Equals(value, pair.Value))
{
continue;
}
// TryGetValue() or Equals failed
return false;
}
// No difference between item and item2
return true;
}
#region PED
public static byte GetPedSpeed(this Ped ped)
{
if (ped.IsSprinting)
{
return 3;
}
if (ped.IsRunning)
{
return 2;
}
if (ped.IsWalking)
{
return 1;
}
return 0;
}
public static Dictionary<byte, short> GetPedClothes(this Ped ped)
{
Dictionary<byte, short> result = new Dictionary<byte, short>();
for (byte i = 0; i < 11; i++)
{
short mod = Function.Call<short>(Hash.GET_PED_DRAWABLE_VARIATION, ped.Handle, i);
result.Add(i, mod);
}
return result;
}
public static PedDataFlags GetPedFlags(this Ped ped)
{
PedDataFlags flags = PedDataFlags.None;
if (ped.IsAiming || ped.IsOnTurretSeat())
{
flags |= PedDataFlags.IsAiming;
}
if (ped.IsReloading)
{
flags |= PedDataFlags.IsReloading;
}
if (ped.IsJumping)
{
flags |= PedDataFlags.IsJumping;
}
// Fake death
if (ped.IsRagdoll || (ped.Health==1 && ped.IsPlayer))
{
flags |= PedDataFlags.IsRagdoll;
}
if (ped.IsOnFire)
{
flags |= PedDataFlags.IsOnFire;
}
if (ped.IsInParachuteFreeFall)
{
flags |= PedDataFlags.IsInParachuteFreeFall;
}
if (ped.ParachuteState == ParachuteState.Gliding)
{
flags |= PedDataFlags.IsParachuteOpen;
}
bool climbingLadder = ped.IsTaskActive(TaskType.CTaskGoToAndClimbLadder);
if (climbingLadder)
{
flags |= PedDataFlags.IsOnLadder;
}
if (ped.IsVaulting && !climbingLadder)
{
flags |= PedDataFlags.IsVaulting;
}
if (ped.IsInCover || ped.IsGoingIntoCover)
{
flags |=PedDataFlags.IsInCover;
}
return flags;
}
public static string[] GetReloadingAnimation(this Ped ped)
{
switch (ped.Weapons.Current.Hash)
{
case WeaponHash.Revolver:
case WeaponHash.RevolverMk2:
case WeaponHash.DoubleActionRevolver:
case WeaponHash.NavyRevolver:
return new string[2] { "anim@weapons@pistol@revolver_str", "reload_aim" };
case WeaponHash.APPistol:
return new string[2] { "weapons@pistol@ap_pistol_str", "reload_aim" };
case WeaponHash.Pistol50:
return new string[2] { "weapons@pistol@pistol_50_str", "reload_aim" };
case WeaponHash.Pistol:
case WeaponHash.PistolMk2:
case WeaponHash.PericoPistol:
case WeaponHash.SNSPistol:
case WeaponHash.SNSPistolMk2:
case WeaponHash.HeavyPistol:
case WeaponHash.VintagePistol:
case WeaponHash.CeramicPistol:
case WeaponHash.MachinePistol:
return new string[2] { "weapons@pistol@pistol_str", "reload_aim" };
case WeaponHash.AssaultRifle:
case WeaponHash.AssaultrifleMk2:
return new string[2] { "weapons@rifle@hi@assault_rifle_str", "reload_aim" };
case WeaponHash.SniperRifle:
return new string[2] { "weapons@rifle@hi@sniper_rifle_str", "reload_aim" };
case WeaponHash.HeavySniper:
case WeaponHash.HeavySniperMk2:
return new string[2] { "weapons@rifle@lo@sniper_heavy_str", "reload_aim" };
case WeaponHash.PumpShotgun:
case WeaponHash.PumpShotgunMk2:
return new string[2] { "weapons@rifle@pump_str", "reload_aim" };
case WeaponHash.Railgun:
return new string[2] { "weapons@rifle@lo@rail_gun_str", "reload_aim" };
case WeaponHash.SawnOffShotgun:
return new string[2] { "weapons@rifle@lo@sawnoff_str", "reload_aim" };
case WeaponHash.AssaultShotgun:
return new string[2] { "weapons@rifle@lo@shotgun_assault_str", "reload_aim" };
case WeaponHash.BullpupShotgun:
return new string[2] { "weapons@rifle@lo@shotgun_bullpup_str", "reload_aim" };
case WeaponHash.AdvancedRifle:
return new string[2] { "weapons@submg@advanced_rifle_str", "reload_aim" };
case WeaponHash.CarbineRifle:
case WeaponHash.CarbineRifleMk2:
case WeaponHash.CompactRifle:
return new string[2] { "weapons@rifle@lo@carbine_str", "reload_aim" };
case WeaponHash.Gusenberg:
return new string[2] { "anim@weapons@machinegun@gusenberg_str", "reload_aim" };
case WeaponHash.Musket:
return new string[2] { "anim@weapons@musket@musket_str", "reload_aim" };
case WeaponHash.FlareGun:
return new string[2] { "anim@weapons@pistol@flare_str", "reload_aim" };
case WeaponHash.SpecialCarbine:
case WeaponHash.SpecialCarbineMk2:
return new string[2] { "anim@weapons@rifle@lo@spcarbine_str", "reload_aim" };
case WeaponHash.CombatPDW:
return new string[2] { "anim@weapons@rifle@lo@pdw_str", "reload_aim" };
case WeaponHash.BullpupRifle:
case WeaponHash.BullpupRifleMk2:
return new string[2] { "anim@weapons@submg@bullpup_rifle_str", "reload_aim" };
case WeaponHash.AssaultSMG:
return new string[2] { "weapons@submg@assault_smg_str", "reload_aim" };
case WeaponHash.MicroSMG:
case WeaponHash.MiniSMG:
return new string[2] { "weapons@submg@lo@micro_smg_str", "reload_aim" };
case WeaponHash.SMG:
case WeaponHash.SMGMk2:
return new string[2] { "weapons@rifle@smg_str", "reload_aim" };
case WeaponHash.GrenadeLauncher:
case WeaponHash.GrenadeLauncherSmoke:
case WeaponHash.CompactGrenadeLauncher:
return new string[2] { "weapons@heavy@grenade_launcher_str", "reload_aim" };
case WeaponHash.RPG:
case WeaponHash.Firework:
return new string[2] { "weapons@heavy@rpg_str", "reload_aim" };
case WeaponHash.CombatMG:
case WeaponHash.CombatMGMk2:
return new string[2] { "weapons@machinegun@combat_mg_str", "reload_aim" };
case WeaponHash.MG:
return new string[2] { "weapons@machinegun@mg_str", "reload_aim" };
default:
Main.Logger.Warning($"~r~Reloading failed! Weapon ~g~[{ped.Weapons.Current.Hash}]~r~ could not be found!");
return null;
}
}
public static VehicleSeat GetNearestSeat(this Ped ped, Vehicle veh, float distanceToignoreDoors = 50f)
{
float num = 99f;
int result = -2;
Dictionary<string, int> dictionary = new Dictionary<string, int>();
dictionary.Add("door_dside_f", -1);
dictionary.Add("door_pside_f", 0);
dictionary.Add("door_dside_r", 1);
dictionary.Add("door_pside_r", 2);
foreach (string text in dictionary.Keys)
{
bool flag = veh.Bones[text].Position != Vector3.Zero;
if (flag)
{
float num2 = ped.Position.DistanceTo(Function.Call<Vector3>(Hash.GET_WORLD_POSITION_OF_ENTITY_BONE, new InputArgument[]
{
veh,
veh.Bones[text].Index
}));
bool flag2 = (num2 < distanceToignoreDoors) && (num2 < num)&& IsSeatUsableByPed(ped, veh, dictionary[text]);
if (flag2)
{
num = num2;
result = dictionary[text];
}
}
}
return (VehicleSeat)result;
}
public static bool IsSeatUsableByPed(Ped ped, Vehicle veh, int _seat)
{
VehicleSeat seat = (VehicleSeat)_seat;
bool result = false;
bool flag = veh.IsSeatFree(seat);
if (flag)
{
result = true;
}
else
{
bool isDead = veh.GetPedOnSeat(seat).IsDead;
if (isDead)
{
result = true;
}
else
{
int num = Function.Call<int>(Hash.GET_RELATIONSHIP_BETWEEN_PEDS, new InputArgument[]
{
ped,
veh.GetPedOnSeat(seat)
});
bool flag2 = num > 2;
if (flag2)
{
result = true;
}
}
}
return result;
}
public static bool IsTaskActive(this Ped p,TaskType task)
{
return Function.Call<bool>(Hash.GET_IS_TASK_ACTIVE, p.Handle, task);
}
public static Vector3 GetAimCoord(this Ped p)
{
var weapon = p.Weapons.CurrentWeaponObject;
var v = p.CurrentVehicle;
// Rhino
if (v!=null && v.Model.Hash==782665360)
{
return v.Bones[35].Position+v.Bones[35].ForwardVector*100;
}
if (p.IsOnTurretSeat()) { return p.GetLookingCoord(); }
if (weapon!=null)
{
// Not very accurate, but doesn't matter
Vector3 dir = weapon.RightVector;
return weapon.Position+dir*20;
}
return GetLookingCoord(p);
}
public static Vector3 GetLookingCoord(this Ped p)
{
EntityBone b = p.Bones[Bone.FacialForehead];
Vector3 v = b.UpVector.Normalized;
return b.Position+200*v;
}
public static void StayInCover(this Ped p)
{
Function.Call(Hash.TASK_STAY_IN_COVER, p);
}
public static VehicleSeat GetSeatTryingToEnter(this Ped p)
{
return (VehicleSeat)Function.Call<int>(Hash.GET_SEAT_PED_IS_TRYING_TO_ENTER, p);
}
#endregion
public static bool IsTurretSeat(this Vehicle veh, int seat)
{
if (!Function.Call<bool>(Hash.DOES_VEHICLE_HAVE_WEAPONS, veh.Handle))
{
return false;
}
switch (seat)
{
case -1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Rhino
|| (VehicleHash)veh.Model.Hash == VehicleHash.Khanjari
|| (VehicleHash)veh.Model.Hash == VehicleHash.FireTruck
|| (VehicleHash)veh.Model.Hash == VehicleHash.Riot2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Cerberus3;
case 0:
return (VehicleHash)veh.Model.Hash == VehicleHash.Apc;
case 1:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Technical3
|| (VehicleHash)veh.Model.Hash == VehicleHash.HalfTrack
|| (VehicleHash)veh.Model.Hash == VehicleHash.Barrage;
case 2:
return (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie
|| (VehicleHash)veh.Model.Hash == VehicleHash.Valkyrie2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Barrage;
case 3:
return (VehicleHash)veh.Model.Hash == VehicleHash.Limo2
|| (VehicleHash)veh.Model.Hash == VehicleHash.Dinghy5;
case 7:
return (VehicleHash)veh.Model.Hash == VehicleHash.Insurgent;
}
return false;
}
public static bool IsOnTurretSeat(this Ped P)
{
if (P.CurrentVehicle == null) { return false; }
return IsTurretSeat(P.CurrentVehicle, (int)P.SeatIndex);
}
}
}

200
Client/Util/Util.cs Normal file
View File

@ -0,0 +1,200 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA.Math;
using GTA;
using GTA.Native;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.InteropServices;
namespace RageCoop.Client
{
internal static class Util
{
#region -- POINTER --
private static int _steeringAngleOffset { get; set; }
public static unsafe void NativeMemory()
{
IntPtr address;
address = Game.FindPattern("\x74\x0A\xF3\x0F\x11\xB3\x1C\x09\x00\x00\xEB\x25", "xxxxxx????xx");
if (address != IntPtr.Zero)
{
_steeringAngleOffset = *(int*)(address + 6) + 8;
}
// breaks some stuff.
/*
address = Game.FindPattern("\x32\xc0\xf3\x0f\x11\x09", "xxxxxx"); // Weapon / Radio slowdown
if (address != IntPtr.Zero)
{
for (int i = 0; i < 6; i++)
{
*(byte*)(address + i).ToPointer() = 0x90;
}
}
*/
}
public static unsafe void CustomSteeringAngle(this Vehicle veh, float value)
{
IntPtr address = new IntPtr((long)veh.MemoryAddress);
if (address == IntPtr.Zero || _steeringAngleOffset == 0)
{
return;
}
*(float*)(address + _steeringAngleOffset).ToPointer() = value;
}
#endregion
#region MATH
public static Vector3 LinearVectorLerp(Vector3 start, Vector3 end, ulong currentTime, int duration)
{
return new Vector3()
{
X = LinearFloatLerp(start.X, end.X, currentTime, duration),
Y = LinearFloatLerp(start.Y, end.Y, currentTime, duration),
Z = LinearFloatLerp(start.Z, end.Z, currentTime, duration),
};
}
public static float LinearFloatLerp(float start, float end, ulong currentTime, int duration)
{
return (end - start) * currentTime / duration + start;
}
public static float Lerp(float from, float to, float fAlpha)
{
return (from * (1.0f - fAlpha)) + (to * fAlpha); //from + (to - from) * fAlpha
}
public static Vector3 RotationToDirection(Vector3 rotation)
{
double z = MathExtensions.DegToRad(rotation.Z);
double x = MathExtensions.DegToRad(rotation.X);
double num = Math.Abs(Math.Cos(x));
return new Vector3
{
X = (float)(-Math.Sin(z) * num),
Y = (float)(Math.Cos(z) * num),
Z = (float)Math.Sin(x)
};
}
#endregion
public static Settings ReadSettings()
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
Settings settings = null;
if (File.Exists(path))
{
using (FileStream stream = File.OpenRead(path))
{
settings = (RageCoop.Client.Settings)ser.Deserialize(stream);
}
using (FileStream stream = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite))
{
ser.Serialize(stream, settings);
}
}
else
{
using (FileStream stream = File.OpenWrite(path))
{
ser.Serialize(stream, settings = new Settings());
}
}
return settings;
}
public static void SaveSettings()
{
try
{
string path = Directory.GetCurrentDirectory() + "\\Scripts\\RageCoop\\RageCoop.Client.Settings.xml";
using (FileStream stream = new FileStream(path, File.Exists(path) ? FileMode.Truncate : FileMode.Create, FileAccess.ReadWrite))
{
XmlSerializer ser = new XmlSerializer(typeof(Settings));
ser.Serialize(stream, Main.Settings);
}
}
catch (Exception ex)
{
GTA.UI.Notification.Show("Error saving player settings: " + ex.Message);
}
}
[DllImport("kernel32.dll")]
public static extern ulong GetTickCount64();
public static Vector3 PredictPosition(this Entity e, bool applyDefault = true)
{
return e.Position+e.Velocity*((applyDefault ? SyncParameters.PositioinPredictionDefault : 0)+Networking.Latency);
}
public static Model ModelRequest(this int hash)
{
Model model = new Model(hash);
if (!model.IsValid)
{
//GTA.UI.Notification.Show("~y~Not valid!");
return null;
}
if (!model.IsLoaded)
{
return model.Request(1000) ? model : null;
}
return model;
}
public static void SetOnFire(this Entity e, bool toggle)
{
if (toggle)
{
Function.Call(Hash.START_ENTITY_FIRE, e.Handle);
}
else
{
Function.Call(Hash.STOP_ENTITY_FIRE, e.Handle);
}
}
public static SyncedPed GetSyncEntity(this Ped p)
{
if (p == null) { return null; }
var c = EntityPool.GetPedByHandle(p.Handle);
if (c==null) { EntityPool.Add(c=new SyncedPed(p)); }
return c;
}
public static SyncedVehicle GetSyncEntity(this Vehicle veh)
{
if (veh == null) { return null; }
var v = EntityPool.GetVehicleByHandle(veh.Handle);
if (v==null) { EntityPool.Add(v=new SyncedVehicle(veh)); }
return v;
}
public static byte GetPlayerRadioIndex()
{
return (byte)Function.Call<int>(Hash.GET_PLAYER_RADIO_STATION_INDEX);
}
public static void SetPlayerRadioIndex(int index)
{
Function.Call(Hash.SET_RADIO_TO_STATION_INDEX, index);
}
}
}

View File

@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA;
using GTA.Native;
using RageCoop.Core;
using GTA.Math;
namespace RageCoop.Client
{
internal static class VehicleExtensions
{
#region VEHICLE
public static VehicleDataFlags GetVehicleFlags(this Vehicle veh)
{
VehicleDataFlags flags = 0;
if (veh.IsEngineRunning)
{
flags |= VehicleDataFlags.IsEngineRunning;
}
if (veh.AreLightsOn)
{
flags |= VehicleDataFlags.AreLightsOn;
}
if (veh.BrakePower >= 0.01f)
{
flags |= VehicleDataFlags.AreBrakeLightsOn;
}
if (veh.AreHighBeamsOn)
{
flags |= VehicleDataFlags.AreHighBeamsOn;
}
if (veh.IsSirenActive)
{
flags |= VehicleDataFlags.IsSirenActive;
}
if (veh.IsDead)
{
flags |= VehicleDataFlags.IsDead;
}
if (Function.Call<bool>(Hash.IS_HORN_ACTIVE, veh.Handle))
{
flags |= VehicleDataFlags.IsHornActive;
}
if (veh.IsSubmarineCar && Function.Call<bool>(Hash._GET_IS_SUBMARINE_VEHICLE_TRANSFORMED, veh.Handle))
{
flags |= VehicleDataFlags.IsTransformed;
}
if (veh.HasRoof && (veh.RoofState == VehicleRoofState.Opened || veh.RoofState == VehicleRoofState.Opening))
{
flags |= VehicleDataFlags.RoofOpened;
}
if (veh.IsAircraft)
{
flags |= VehicleDataFlags.IsAircraft;
}
if (veh.Model.Hash==1483171323 && veh.IsDeluxoHovering())
{
flags|= VehicleDataFlags.IsDeluxoHovering;
}
return flags;
}
public static bool HasFlag(this PedDataFlags flagToCheck, PedDataFlags flag)
{
return (flagToCheck & flag)!=0;
}
public static bool HasFlag(this VehicleDataFlags flagToCheck, VehicleDataFlags flag)
{
return (flagToCheck & flag)!=0;
}
public static Dictionary<uint, bool> GetWeaponComponents(this Weapon weapon)
{
Dictionary<uint, bool> result = null;
if (weapon.Components.Count > 0)
{
result = new Dictionary<uint, bool>();
foreach (var comp in weapon.Components)
{
result.Add((uint)comp.ComponentHash, comp.Active);
}
}
return result;
}
public static Dictionary<int, int> GetVehicleMods(this VehicleModCollection mods)
{
Dictionary<int, int> result = new Dictionary<int, int>();
foreach (VehicleMod mod in mods.ToArray())
{
result.Add((int)mod.Type, mod.Index);
}
return result;
}
public static VehicleDamageModel GetVehicleDamageModel(this Vehicle veh)
{
// Broken windows
byte brokenWindows = 0;
for (int i = 0; i < 8; i++)
{
if (!veh.Windows[(VehicleWindowIndex)i].IsIntact)
{
brokenWindows |= (byte)(1 << i);
}
}
// Broken doors
byte brokenDoors = 0;
byte openedDoors = 0;
foreach (VehicleDoor door in veh.Doors)
{
if (door.IsBroken)
{
brokenDoors |= (byte)(1 << (byte)door.Index);
}
else if (door.IsOpen)
{
openedDoors |= (byte)(1 << (byte)door.Index);
}
}
// Bursted tires
short burstedTires = 0;
foreach (VehicleWheel wheel in veh.Wheels.GetAllWheels())
{
if (wheel.IsBursted)
{
burstedTires |= (short)(1 << (int)wheel.BoneId);
}
}
return new VehicleDamageModel()
{
BrokenDoors = brokenDoors,
OpenedDoors = openedDoors,
BrokenWindows = brokenWindows,
BurstedTires = burstedTires,
LeftHeadLightBroken = (byte)(veh.IsLeftHeadLightBroken ? 1 : 0),
RightHeadLightBroken = (byte)(veh.IsRightHeadLightBroken ? 1 : 0)
};
}
public static Dictionary<int, int> GetPassengers(this Vehicle veh)
{
Dictionary<int, int> ps = new Dictionary<int, int>();
var d = veh.Driver;
if (d!=null&&d.IsSittingInVehicle())
{
ps.Add(-1, d.GetSyncEntity().ID);
}
foreach (Ped p in veh.Passengers)
{
if (p.IsSittingInVehicle())
{
ps.Add((int)p.SeatIndex, (int)p.GetSyncEntity().ID);
}
}
return ps;
}
public static void SetDeluxoHoverState(this Vehicle deluxo, bool hover)
{
Function.Call(Hash._SET_VEHICLE_HOVER_TRANSFORM_PERCENTAGE, deluxo, hover ? 1f : 0f);
}
public static bool IsDeluxoHovering(this Vehicle deluxo)
{
return Math.Abs(deluxo.Bones[27].ForwardVector.GetCosTheta(deluxo.ForwardVector)-1)>0.05;
}
public static void SetDeluxoWingRatio(this Vehicle v, float ratio)
{
Function.Call(Hash._SET_SPECIALFLIGHT_WING_RATIO, v, ratio);
}
public static float GetDeluxoWingRatio(this Vehicle v)
{
return v.Bones[99].Position.DistanceTo(v.Bones[92].Position)-1.43f;
}
public static float GetNozzleAngel(this Vehicle plane)
{
return Function.Call<float>(Hash._GET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane);
}
public static bool HasNozzle(this Vehicle v)
{
switch (v.Model.Hash)
{
// Hydra
case 970385471:
return true;
// Avenger
case -2118308144:
return true;
// Tula
case 1043222410:
return true;
// Avenger
case 408970549:
return true;
}
return false;
}
public static void SetNozzleAngel(this Vehicle plane, float ratio)
{
Function.Call(Hash.SET_VEHICLE_FLIGHT_NOZZLE_POSITION, plane, ratio);
}
public static void SetDamageModel(this Vehicle veh, VehicleDamageModel model, bool leavedoors = true)
{
for (int i = 0; i < 8; i++)
{
var door = veh.Doors[(VehicleDoorIndex)i];
if ((model.BrokenDoors & (byte)(1 << i)) != 0)
{
door.Break(leavedoors);
}
else if (door.IsBroken)
{
// The vehicle can only fix a door if the vehicle was completely fixed
veh.Repair();
return;
}
if ((model.OpenedDoors & (byte)(1 << i)) != 0)
{
if ((!door.IsOpen)&&(!door.IsBroken))
{
door.Open();
}
}
else if (door.IsOpen)
{
if (!door.IsBroken) { door.Close(); }
}
if ((model.BrokenWindows & (byte)(1 << i)) != 0)
{
veh.Windows[(VehicleWindowIndex)i].Smash();
}
else if (!veh.Windows[(VehicleWindowIndex)i].IsIntact)
{
veh.Windows[(VehicleWindowIndex)i].Repair();
}
}
foreach (VehicleWheel wheel in veh.Wheels)
{
if ((model.BurstedTires & (short)(1 << (int)wheel.BoneId)) != 0)
{
if (!wheel.IsBursted)
{
wheel.Puncture();
wheel.Burst();
}
}
else if (wheel.IsBursted)
{
wheel.Fix();
}
}
veh.IsLeftHeadLightBroken = model.LeftHeadLightBroken > 0;
veh.IsRightHeadLightBroken = model.RightHeadLightBroken > 0;
}
#endregion
}
}

View File

@ -31,29 +31,85 @@ namespace RageCoop.Client
}
return p.Bones[Bone.SkelRightHand].Position;
}
static long BulletsShot=0;
public static MuzzleInfo GetMuzzleInfo(this Vehicle v)
{
BulletsShot++;
int i;
switch (v.Model.Hash)
{
// SCRAMJET
case -638562243:
i=BulletsShot%2==0 ? 44 : 45;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// VIGILANTE
case -1242608589:
i=BulletsShot%2==0 ? 42 : 43;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR380
case 540101442:
i=BulletsShot%2==0 ? 57 : 63;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR3802
case -1106120762:
i=BulletsShot%2==0 ? 57 : 63;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ZR3803
case -1478704292:
i=BulletsShot%2==0 ? 53 : 59;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// STROMBERG
case 886810209:
i=BulletsShot%2==0 ? 85 : 84;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// SLAMVAN4
case -2061049099:
i=BulletsShot%2==0 ? 76 : 78;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPERATOR
case 444994115:
i=BulletsShot%2==0 ? 88 : 86;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// IMPALER2
case 1009171724:
i=BulletsShot%2==0 ? 63 : 64;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// DOMINATOR4
case -688189648:
i=BulletsShot%2==0 ? 59 : 60;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// SAVAGE
case -82626025:
return new MuzzleInfo(v.Bones[30].Position, v.Bones[30].ForwardVector);
// BUZZARD
case 788747387:
i=Main.Ticked%2==0 ? 28 : 23;
i=BulletsShot%2==0 ? 28 : 23;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// ANNIHL
case 837858166:
i=(int)Main.Ticked%4+35;
i=(int)BulletsShot%4+35;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// HYDRA
case 970385471:
i=Main.Ticked%2==0 ? 29 : 28;
i=BulletsShot%2==0 ? 29 : 28;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// STARLING
case -1700874274:
i=Main.Ticked%2==0 ? 24 : 12;
i=BulletsShot%2==0 ? 24 : 12;
return new MuzzleInfo(v.Bones[i].Position, v.Bones[i].ForwardVector);
// RHINO
@ -78,25 +134,7 @@ namespace RageCoop.Client
return w.Group==WeaponGroup.Thrown || ProjectileWeapons.Contains(w.Hash);
}
}
public static int GetDamage(this Weapon w)
{
int damage = 0;
switch (w.Group)
{
case WeaponGroup.AssaultRifle: damage=30; break;
case WeaponGroup.Heavy: damage=30; break;
case WeaponGroup.MG: damage=30; break;
case WeaponGroup.PetrolCan: damage=0; break;
case WeaponGroup.Pistol: damage=30; break;
case WeaponGroup.Shotgun: damage=30; break;
case WeaponGroup.SMG: damage=20; break;
case WeaponGroup.Sniper: damage=100; break;
case WeaponGroup.Thrown: damage=0; break;
case WeaponGroup.Unarmed: damage=0; break;
}
return damage;
}
public static readonly Dictionary<WeaponHash, int> MuzzleBoneIndexes = new Dictionary<WeaponHash, int>
{
{WeaponHash.HeavySniper,6},
@ -181,6 +219,8 @@ namespace RageCoop.Client
VehicleWeaponHash.PlaneRocket,
VehicleWeaponHash.SpaceRocket,
VehicleWeaponHash.Tank,
(VehicleWeaponHash)3565779982, // STROMBERG missiles
(VehicleWeaponHash)3169388763, // SCRAMJET missiles
};
}
}

View File

@ -49,7 +49,7 @@ namespace RageCoop.Client
// Values of 2.0 or more make for very aggressive waves like you see during a thunderstorm.
Function.Call(Hash.SET_DEEP_OCEAN_SCALER, 0.0f); // Works only ~200 meters around the player
Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, Game.Player.Character.Handle, true, false);
// Function.Call(Hash.SET_CAN_ATTACK_FRIENDLY, Game.Player.Character.Handle, true, false);
if (Main.Settings==null) { return; }
if (Main.Settings.DisableTraffic)
{
@ -99,8 +99,8 @@ namespace RageCoop.Client
Function.Call(Hash.SET_RANDOM_BOATS, false);
Function.Call(Hash.SET_GARBAGE_TRUCKS, false);
Function.Call(Hash.DELETE_ALL_TRAINS);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 0);
Function.Call(Hash.SET_PED_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_VEHICLE_POPULATION_BUDGET, 3);
Function.Call(Hash.SET_ALL_LOW_PRIORITY_VEHICLE_GENERATORS_ACTIVE, false);
Function.Call(Hash.SET_FAR_DRAW_VEHICLES, false);
Function.Call(Hash.SET_NUMBER_OF_PARKED_VEHICLES, 0);
@ -126,6 +126,11 @@ namespace RageCoop.Client
foreach (Vehicle veh in World.GetAllVehicles())
{
SyncedVehicle v = veh.GetSyncEntity();
if (v.MainVehicle==Game.Player.LastVehicle)
{
// Don't delete player's vehicle
continue;
}
if((v== null) || (v.IsMine&&veh.PopulationType!=EntityPopulationType.Mission))
{
Main.Logger.Debug($"Removing Vehicle {veh.Handle}. Reason:ClearTraffic");

View File

@ -1,6 +1,7 @@
using System;
using System.Text;
using System.Linq;
using GTA.Math;
namespace RageCoop.Core
{
@ -103,18 +104,18 @@ namespace RageCoop.Core
return value;
}
public LVector3 ReadLVector3()
public Vector3 ReadVector3()
{
return new LVector3()
return new Vector3()
{
X = ReadFloat(),
Y = ReadFloat(),
Z = ReadFloat()
};
}
public LQuaternion ReadLQuaternion()
public Quaternion ReadQuaternion()
{
return new LQuaternion()
return new Quaternion()
{
X = ReadFloat(),
Y = ReadFloat(),

View File

@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GTA.Math;
using System.Net;
namespace RageCoop.Core
{
public class CoreUtils
@ -34,16 +36,17 @@ namespace RageCoop.Core
return (0x0, null);
}
}
}
public static class Extensions
{
public static void AddLVector3(this List<byte> bytes, LVector3 vec3)
public static void AddVector3(this List<byte> bytes, Vector3 vec3)
{
bytes.AddRange(BitConverter.GetBytes(vec3.X));
bytes.AddRange(BitConverter.GetBytes(vec3.Y));
bytes.AddRange(BitConverter.GetBytes(vec3.Z));
}
public static void AddLQuaternion(this List<byte> bytes, LQuaternion quat)
public static void AddQuaternion(this List<byte> bytes, Quaternion quat)
{
bytes.AddRange(BitConverter.GetBytes(quat.X));
bytes.AddRange(BitConverter.GetBytes(quat.Y));

View File

@ -5,48 +5,35 @@ using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
namespace RageCoop.Core
namespace RageCoop.Core.Logging
{
public class Loggger
public class Logger :IDisposable
{
public string LogPath;
private StreamWriter logWriter;
private bool UseConsole=false;
/// <summary>
/// 0:Trace, 1:Debug, 2:Info, 3:Warning, 4:Error
/// </summary>
public int LogLevel = 0;
public string LogPath;
public bool UseConsole = false;
private static StreamWriter logWriter;
private string Buffer="";
private Thread LoggerThread;
private bool Stopping=false;
public Loggger(string path,bool overwrite=true)
public Logger(bool overwrite=true)
{
LogPath=path;
if (File.Exists(path)&&overwrite) { File.Delete(path); }
Task.Run(() =>
if (File.Exists(LogPath)&&overwrite) { File.Delete(LogPath); }
LoggerThread=new Thread(() =>
{
while (true)
{
Flush();
Thread.Sleep(1000);
}
});
}
public Loggger()
{
UseConsole=true;
Task.Run(() =>
{
while (true)
while (!Stopping)
{
Flush();
Thread.Sleep(1000);
}
});
LoggerThread.Start();
}
public void Info(string message)
@ -146,5 +133,10 @@ namespace RageCoop.Core
}
}
public void Dispose()
{
Stopping=true;
LoggerThread?.Join();
}
}
}

View File

@ -7,6 +7,7 @@ using GTA.Math;
namespace RageCoop.Core
{
/*
/// <summary>
///
/// </summary>
@ -111,7 +112,7 @@ namespace RageCoop.Core
/// </summary>
public float W { get; set; }
}
*/
public enum PacketTypes:byte
{
Handshake=0,
@ -148,6 +149,7 @@ namespace RageCoop.Core
EnteredVehicle=34,
OwnerChanged=35,
VehicleBulletShot = 36,
NozzleTransform=37,
#endregion
@ -205,7 +207,7 @@ namespace RageCoop.Core
RoofOpened = 1 << 8,
OnTurretSeat = 1 << 9,
IsAircraft = 1 << 10,
IsHandBrakeOn=1<<11,
IsDeluxoHovering=1 << 11,
}
@ -236,10 +238,6 @@ namespace RageCoop.Core
{
public class Mod : Packet
{
public long NetHandle { get; set; }
public long Target { get; set; }
public string Name { get; set; }
public byte CustomPacketID { get; set; }
@ -253,12 +251,6 @@ namespace RageCoop.Core
List<byte> byteArray = new List<byte>();
// Write NetHandle
byteArray.AddRange(BitConverter.GetBytes(NetHandle));
// Write Target
byteArray.AddRange(BitConverter.GetBytes(Target));
// Write Name
byte[] nameBytes = Encoding.UTF8.GetBytes(Name);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
@ -283,12 +275,6 @@ namespace RageCoop.Core
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
// Read NetHandle
NetHandle = reader.ReadLong();
// Read Target
Target = reader.ReadLong();
// Read Name
int nameLength = reader.ReadInt();
Name = reader.ReadString(nameLength);
@ -492,10 +478,10 @@ namespace RageCoop.Core
byteArray.AddRange(BitConverter.GetBytes(stringBytes.Length));
byteArray.AddRange(stringBytes);
}
else if (type == typeof(LVector3))
else if (type == typeof(Vector3))
{
byteArray.Add(0x04);
LVector3 vector = (LVector3)x;
Vector3 vector = (Vector3)x;
byteArray.AddRange(BitConverter.GetBytes(vector.X));
byteArray.AddRange(BitConverter.GetBytes(vector.Y));
byteArray.AddRange(BitConverter.GetBytes(vector.Z));
@ -539,7 +525,7 @@ namespace RageCoop.Core
Args.Add(reader.ReadString(stringLength));
break;
case 0x04:
Args.Add(new LVector3()
Args.Add(new Vector3()
{
X = reader.ReadFloat(),
Y = reader.ReadFloat(),
@ -602,10 +588,10 @@ namespace RageCoop.Core
byteArray.AddRange(BitConverter.GetBytes(stringBytes.Length));
byteArray.AddRange(stringBytes);
}
else if (type == typeof(LVector3))
else if (type == typeof(Vector3))
{
byteArray.Add(0x04);
LVector3 vector = (LVector3)x;
Vector3 vector = (Vector3)x;
byteArray.AddRange(BitConverter.GetBytes(vector.X));
byteArray.AddRange(BitConverter.GetBytes(vector.Y));
byteArray.AddRange(BitConverter.GetBytes(vector.Z));
@ -657,7 +643,7 @@ namespace RageCoop.Core
Args.Add(reader.ReadString(stringLength));
break;
case 0x04:
Args.Add(new LVector3()
Args.Add(new Vector3()
{
X = reader.ReadFloat(),
Y = reader.ReadFloat(),
@ -680,9 +666,6 @@ namespace RageCoop.Core
#endregion // ===== NATIVECALL =====
}
/// <summary>
/// ?
/// </summary>
public static class CoopSerializer
{
/// <summary>

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
@ -126,17 +126,17 @@ namespace RageCoop.Core
public int Health { get; set; }
public LVector3 Position { get; set; }
public Vector3 Position { get; set; }
public LVector3 Rotation { get; set; }
public Vector3 Rotation { get; set; }
public LVector3 Velocity { get; set; }
public Vector3 Velocity { get; set; }
public LVector3 RotationVelocity { get; set; }
public Vector3 RotationVelocity { get; set; }
public byte Speed { get; set; }
public LVector3 AimCoords { get; set; }
public Vector3 AimCoords { get; set; }
public uint CurrentWeaponHash { get; set; }
@ -160,17 +160,17 @@ namespace RageCoop.Core
byteArray.AddRange(BitConverter.GetBytes(Health));
// Write ped position
byteArray.AddLVector3(Position);
byteArray.AddVector3(Position);
// Write ped rotation
byteArray.AddLVector3(Rotation);
byteArray.AddVector3(Rotation);
// Write ped velocity
byteArray.AddLVector3(Velocity);
byteArray.AddVector3(Velocity);
if (Flag.HasFlag(PedDataFlags.IsRagdoll))
{
byteArray.AddLVector3(RotationVelocity);
byteArray.AddVector3(RotationVelocity);
}
// Write ped speed
@ -182,7 +182,7 @@ namespace RageCoop.Core
if (Flag.HasFlag(PedDataFlags.IsAiming))
{
// Write ped aim coords
byteArray.AddLVector3(AimCoords);
byteArray.AddVector3(AimCoords);
}
byteArray.AddFloat(Heading);
@ -209,18 +209,18 @@ namespace RageCoop.Core
Health = reader.ReadInt();
// Read player position
Position = reader.ReadLVector3();
Position = reader.ReadVector3();
// Read player rotation
Rotation = reader.ReadLVector3();
Rotation = reader.ReadVector3();
// Read player velocity
Velocity = reader.ReadLVector3();
Velocity = reader.ReadVector3();
// Read rotation velocity if in ragdoll
if (Flag.HasFlag(PedDataFlags.IsRagdoll))
{
RotationVelocity=reader.ReadLVector3();
RotationVelocity=reader.ReadVector3();
}
// Read player speed
@ -233,7 +233,7 @@ namespace RageCoop.Core
if (Flag.HasFlag(PedDataFlags.IsAiming))
{
// Read player aim coords
AimCoords = reader.ReadLVector3();
AimCoords = reader.ReadVector3();
}
Heading=reader.ReadFloat();

View File

@ -16,8 +16,6 @@ namespace RageCoop.Core
public string ModVersion { get; set; }
public bool NPCsAllowed { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
@ -38,9 +36,6 @@ namespace RageCoop.Core
byteArray.AddRange(BitConverter.GetBytes(modVersionBytes.Length));
byteArray.AddRange(modVersionBytes);
// Write NpcsAllowed
byteArray.Add(NPCsAllowed ? (byte)0x01 : (byte)0x00);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
@ -63,9 +58,6 @@ namespace RageCoop.Core
// Read ModVersion
int modVersionLength = reader.ReadInt();
ModVersion = reader.ReadString(modVersionLength);
// Read NPCsAllowed
NPCsAllowed = reader.ReadBool();
#endregion
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
@ -15,11 +16,11 @@ namespace RageCoop.Core
public int ShooterID { get; set; }
public uint WeaponHash { get; set; }
public LVector3 Position { get; set; }
public Vector3 Position { get; set; }
public LVector3 Rotation { get; set; }
public Vector3 Rotation { get; set; }
public LVector3 Velocity { get; set; }
public Vector3 Velocity { get; set; }
public bool Exploded { get; set; }
@ -41,14 +42,14 @@ namespace RageCoop.Core
byteArray.AddUint(WeaponHash);
// Write position
byteArray.AddLVector3(Position);
byteArray.AddVector3(Position);
// Write rotation
byteArray.AddLVector3(Rotation);
byteArray.AddVector3(Rotation);
// Write velocity
byteArray.AddLVector3(Velocity);
byteArray.AddVector3(Velocity);
if (Exploded) { byteArray.Add(1); }
@ -73,13 +74,13 @@ namespace RageCoop.Core
WeaponHash= reader.ReadUInt();
// Read position
Position = reader.ReadLVector3();
Position = reader.ReadVector3();
// Read rotation
Rotation = reader.ReadLVector3();
Rotation = reader.ReadVector3();
// Read velocity
Velocity =reader.ReadLVector3();
Velocity =reader.ReadVector3();
if (reader.CanRead(1))
{

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
@ -15,8 +15,8 @@ namespace RageCoop.Core
public uint WeaponHash { get; set; }
public LVector3 StartPosition { get; set; }
public LVector3 EndPosition { get; set; }
public Vector3 StartPosition { get; set; }
public Vector3 EndPosition { get; set; }
public override void Pack(NetOutgoingMessage message)
{
@ -32,10 +32,10 @@ namespace RageCoop.Core
byteArray.AddRange(BitConverter.GetBytes(WeaponHash));
// Write StartPosition
byteArray.AddLVector3(StartPosition);
byteArray.AddVector3(StartPosition);
// Write EndPosition
byteArray.AddLVector3(EndPosition);
byteArray.AddVector3(EndPosition);
byte[] result = byteArray.ToArray();
@ -57,10 +57,10 @@ namespace RageCoop.Core
WeaponHash=reader.ReadUInt();
// Read StartPosition
StartPosition=reader.ReadLVector3();
StartPosition=reader.ReadVector3();
// Read EndPosition
EndPosition=reader.ReadLVector3();
EndPosition=reader.ReadVector3();
#endregion
}
}

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace RageCoop.Core
{
public partial class Packets
{
public class NozzleTransform : Packet
{
public int VehicleID { get; set; }
public bool Hover { get; set; }
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.NozzleTransform);
List<byte> byteArray = new List<byte>();
byteArray.AddInt(VehicleID);
if (Hover) { byteArray.Add(1); }
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void Unpack(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
VehicleID=reader.ReadInt();
Hover=reader.CanRead(1);
#endregion
}
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using GTA;
using GTA.Math;
using Lidgren.Network;
namespace RageCoop.Core
@ -40,6 +41,7 @@ namespace RageCoop.Core
/// </summary>
public Dictionary<int, int> Passengers { get; set; }
public byte RadioStation { get; set; } = 255;
public override void Pack(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
@ -117,6 +119,9 @@ namespace RageCoop.Core
// Write LockStatus
byteArray.Add((byte)LockStatus);
// Write RadioStation
byteArray.Add(RadioStation);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
@ -199,6 +204,9 @@ namespace RageCoop.Core
// Read LockStatus
LockStatus=(VehicleLockStatus)reader.ReadByte();
// Read RadioStation
RadioStation=reader.ReadByte();
#endregion
}
}
@ -207,18 +215,19 @@ namespace RageCoop.Core
{
public int ID { get; set; }
public LVector3 Position { get; set; }
public Vector3 Position { get; set; }
public LVector3 Rotation { get; set; }
public Quaternion Quaternion { get; set; }
// public Vector3 Rotation { get; set; }
public LVector3 Velocity { get; set; }
public Vector3 Velocity { get; set; }
public LVector3 RotationVelocity { get; set; }
public Vector3 RotationVelocity { get; set; }
public float ThrottlePower { get; set; }
public float BrakePower { get; set; }
public float SteeringAngle { get; set; }
public float DeluxoWingRatio { get; set; } = -1;
public override void Pack(NetOutgoingMessage message)
{
@ -231,17 +240,18 @@ namespace RageCoop.Core
byteArray.AddInt(ID);
// Write position
byteArray.AddLVector3(Position);
byteArray.AddVector3(Position);
// Write rotation
byteArray.AddLVector3(Rotation);
// Write quaternion
//byteArray.AddVector3(Rotation);
byteArray.AddQuaternion(Quaternion);
// Write velocity
byteArray.AddLVector3(Velocity);
byteArray.AddVector3(Velocity);
// Write rotation velocity
byteArray.AddLVector3(RotationVelocity);
byteArray.AddVector3(RotationVelocity);
byteArray.AddFloat(ThrottlePower);
@ -251,6 +261,11 @@ namespace RageCoop.Core
// Write vehicle steering angle
byteArray.AddFloat(SteeringAngle);
if (DeluxoWingRatio!=-1)
{
byteArray.AddFloat(DeluxoWingRatio);
}
byte[] result = byteArray.ToArray();
@ -268,16 +283,17 @@ namespace RageCoop.Core
ID = reader.ReadInt();
// Read position
Position = reader.ReadLVector3();
Position = reader.ReadVector3();
// Read rotation
Rotation = reader.ReadLVector3();
// Read quaternion
// Rotation = reader.ReadVector3();
Quaternion=reader.ReadQuaternion();
// Read velocity
Velocity =reader.ReadLVector3();
Velocity =reader.ReadVector3();
// Read rotation velocity
RotationVelocity=reader.ReadLVector3();
RotationVelocity=reader.ReadVector3();
// Read throttle power
ThrottlePower=reader.ReadFloat();
@ -288,6 +304,11 @@ namespace RageCoop.Core
// Read steering angle
SteeringAngle = reader.ReadFloat();
if (reader.CanRead(4))
{
DeluxoWingRatio= reader.ReadFloat();
}
#endregion
}
}

View File

@ -1,34 +1,33 @@
using System.Linq;
using System.Runtime.CompilerServices;
using GTA.Math;
[assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")]
namespace RageCoop.Core
{
public class PlayerData
{
public string Username { get; set; }
public string Username { get; internal set; }
/// <summary>
/// Universal character ID.
/// </summary>
public int PedID
{
get; set;
get; internal set;
}
/// <summary>
/// Universal vehicle ID.
/// The ID of player's last vehicle.
/// </summary>
public int VehicleID { get; set; }
public bool IsInVehicle { get; internal set; }
public LVector3 Position { get; set; }
public int VehicleID { get; internal set; }
public Vector3 Position { get;internal set; }
/// <summary>
/// Player Latency in second.
/// </summary>
public float Latency { get; set; }
public int Health { get; set; }
public bool IsInRangeOf(LVector3 position, float distance)
{
return LVector3.Subtract(Position, position).Length() < distance;
}
public float Latency { get; internal set; }
public int Health { get; internal set; }
}
}

View File

@ -0,0 +1,153 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Linq;
[assembly: InternalsVisibleTo("RageCoop.Server")]
[assembly: InternalsVisibleTo("RageCoop.Client")]
namespace RageCoop.Core.Scripting
{
internal class ScriptingEngine
{
private Type BaseScriptType;
public Logging.Logger Logger { get; set; }
public ScriptingEngine(Type baseScriptType, Logging.Logger logger)
{
BaseScriptType = baseScriptType;
Logger = logger;
}
/// <summary>
/// Loads scripts from the specified assembly file.
/// </summary>
/// <param name="filename">The path to the assembly file to load.</param>
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
private bool LoadScriptsFromAssembly(string filename)
{
if (!IsManagedAssembly(filename))
return false;
Logger?.Debug($"Loading assembly {Path.GetFileName(filename)} ...");
Assembly assembly;
try
{
assembly = Assembly.LoadFrom(filename);
}
catch (Exception ex)
{
Logger?.Error( "Unable to load "+Path.GetFileName(filename));
Logger?.Error(ex);
return false;
}
return LoadScriptsFromAssembly(assembly, filename);
}
/// <summary>
/// Loads scripts from the specified assembly object.
/// </summary>
/// <param name="filename">The path to the file associated with this assembly.</param>
/// <param name="assembly">The assembly to load.</param>
/// <returns><see langword="true" /> on success, <see langword="false" /> otherwise</returns>
private bool LoadScriptsFromAssembly(Assembly assembly, string filename)
{
int count = 0;
try
{
// Find all script types in the assembly
foreach (var type in assembly.GetTypes().Where(x => IsSubclassOf(x,nameof(BaseScriptType))))
{
ConstructorInfo constructor = type.GetConstructor(System.Type.EmptyTypes);
if (constructor != null && constructor.IsPublic)
{
try
{
// Invoke script constructor
constructor.Invoke(null);
count++;
}
catch (Exception ex)
{
Logger?.Error($"Error occurred when loading script: {type.FullName}.");
Logger?.Error(ex);
}
}
else
{
Logger?.Error($"Script {type.FullName} has an invalid contructor.");
}
}
}
catch (ReflectionTypeLoadException ex)
{
Logger?.Error($"Failed to load assembly {Path.GetFileName(filename)}: ");
Logger?.Error(ex);
return false;
}
Logger?.Info($"Loaded {count.ToString()} script(s) in {Path.GetFileName(filename)}");
return count != 0;
}
private bool IsManagedAssembly(string filename)
{
try
{
using (Stream file = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
if (file.Length < 64)
return false;
using (BinaryReader bin = new BinaryReader(file))
{
// PE header starts at offset 0x3C (60). Its a 4 byte header.
file.Position = 0x3C;
uint offset = bin.ReadUInt32();
if (offset == 0)
offset = 0x80;
// Ensure there is at least enough room for the following structures:
// 24 byte PE Signature & Header
// 28 byte Standard Fields (24 bytes for PE32+)
// 68 byte NT Fields (88 bytes for PE32+)
// >= 128 byte Data Dictionary Table
if (offset > file.Length - 256)
return false;
// Check the PE signature. Should equal 'PE\0\0'.
file.Position = offset;
if (bin.ReadUInt32() != 0x00004550)
return false;
// Read PE magic number from Standard Fields to determine format.
file.Position += 20;
var peFormat = bin.ReadUInt16();
if (peFormat != 0x10b /* PE32 */ && peFormat != 0x20b /* PE32Plus */)
return false;
// Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
// When this is non-zero then the file contains CLI data otherwise not.
file.Position = offset + (peFormat == 0x10b ? 232 : 248);
return bin.ReadUInt32() != 0;
}
}
}
catch
{
// This is likely not a valid assembly if any IO exceptions occur during reading
return false;
}
}
private bool IsSubclassOf(Type type, string baseTypeName)
{
for (Type t = type.BaseType; t != null; t = t.BaseType)
if (t.FullName == baseTypeName)
return true;
return false;
}
}
}

View File

@ -1,57 +1,94 @@
# 🌐 RAGECOOP
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
# Disclaimer
The original author of this project is @EntenKoeniq.
The project has been reworked and is currently maintained by @Sardelka9515.
# **Help to survive the project**
# 🧠 That's it
RAGECOOP is a multiplayer mod to play story mode or some mods made for RAGECOOP or just drive around with your buddy.
#### The project is in active development as for now but may get discontinued if user's support is not present!
## if you are a developer...
Please **CONTRIBUTE** to the project and make RAGECOOP better together!
## otherwise...
**[Become a patreon](https://www.patreon.com/Sardelka)** to help keep the project alive and get exclusive support to individual issues.
<br><br><br>
_Old name: GTACOOP:R_
# 📋 Requirements
- Visual Studio 2022
- .NET 6.0
- .NET Framework 4.8
# 📚 Libraries
- [ScriptHookVDotNet3](https://github.com/crosire/scripthookvdotnet/releases/tag/v3.4.0)
- [LemonUI.SHVDN3](https://github.com/justalemon/LemonUI/releases/tag/v1.6)
- Lidgren Network Custom (***PRIVATE***)
- - No new features (only improvements)
- [Newtonsoft.Json](https://www.nuget.org/packages/Newtonsoft.Json/13.0.1)
- [ClearScript](https://github.com/microsoft/ClearScript)
# Features
1. Synchronized bullets
2. Synchronized vehicle/player/NPC
3. Synchronized Projectile
4. Basic ragdoll sync
3. Synchronized projectiles
4. Simple ragdoll sync
5. Smoother vehicle/ped movement.
6. Ownership based sync logic, carjacking is now working properly.
7. Introduced SyncEvents.
8. Code refactoring and namespace cleanup
9. Synchronized vehicle doors, brake and throttle.
10. Other improvements
10. Weaponized vehicle sync(WIP).
11. Other improvements
# Known issues
1. Weapon sounds are missing.
2. Cover sync is still buggy.
3. Weaponized vehicle doen't work currently
4. Framerate drop with high number of synchronized entities.
3. Framerate drop with high number of synchronized entities.
5. Scripting API is screwed.(will be rewritten in the future)
# Installation
## Client
## Installation
### Client
Download latest release, remove old version of the mod. Extract the zip and put `RageCoop` in `Grand Theft Auto V/Scripts`.
## Server
### Server
Download latest release for your OS, then extract and run.
# Downloads
Old release can be downloaded [here](https://gitlab.com/justasausage/RageCOOP-V/-/tree/main/Release)
Download latest release [here](https://github.com/RAGECOOP/RAGECOOP-V/releases/latest)
Please note that this is incompatible with all previous versions of ragecoop, remove old files before installing.
# Support us
<a href="https://patreon.com/Sardelka"><img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3DSardelka%26type%3Dpatrons&style=for-the-badge" /></a>
# 🦆 Special thanks to
- [Makinolo](https://github.com/Makinolo), [oldnapalm](https://github.com/oldnapalm)
- - For testing, ideas, contributions and the first modification with the API
- [crosire](https://github.com/crosire)
- - For the extensive work in ScriptHookVDotNet
- [justalemon](https://github.com/justalemon)
- - For the extensive work in LemonUI
# 📝 License
This project is licensed under [MIT license](https://github.com/RAGECOOP/RAGECOOP-V/blob/main/LICENSE)
[contributors-shield]: https://img.shields.io/github/contributors/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[contributors-url]: https://github.com/RAGECOOP/RAGECOOP-V/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[forks-url]: https://github.com/RAGECOOP/RAGECOOP-V/network/members
[stars-shield]: https://img.shields.io/github/stars/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[stars-url]: https://github.com/RAGECOOP/RAGECOOP-V/stargazers
[issues-shield]: https://img.shields.io/github/issues/RAGECOOP/RAGECOOP-V.svg?style=for-the-badge
[issues-url]: https://github.com/RAGECOOP/RAGECOOP-V/issues

View File

@ -1,9 +1 @@
using System.Collections.Generic;
namespace RageCoop.Server
{
public class Allowlist
{
public List<string> Username { get; set; } = new();
}
}

View File

@ -2,23 +2,25 @@
using System.Collections.Generic;
using RageCoop.Core;
using Lidgren.Network;
using GTA.Math;
namespace RageCoop.Server
{
public class Client
{
public long ClientID = 0;
public long NetID = 0;
private float _currentLatency = 0f;
public NetConnection Connection { get; set; }
public float Latency
{
get => _currentLatency;
set
internal set
{
_currentLatency = value;
if ((value * 1000f) > Server.MainSettings.MaxLatency)
{
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID)?.Disconnect($"Too high latency [{value * 1000f}/{(float)Server.MainSettings.MaxLatency}]");
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID)?.Disconnect($"Too high latency [{value * 1000f}/{(float)Server.MainSettings.MaxLatency}]");
}
}
}
@ -26,7 +28,7 @@ namespace RageCoop.Server
private readonly Dictionary<string, object> _customData = new();
private long _callbacksCount = 0;
public readonly Dictionary<long, Action<object>> Callbacks = new();
public bool FilesReceived { get; set; } = false;
public bool FilesReceived { get;internal set; } = false;
public bool FilesSent = false;
#region CUSTOMDATA FUNCTIONS
@ -64,7 +66,7 @@ namespace RageCoop.Server
#region FUNCTIONS
public void Kick(string reason)
{
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID)?.Disconnect(reason);
Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID)?.Disconnect(reason);
}
public void Kick(string[] reason)
{
@ -75,7 +77,7 @@ namespace RageCoop.Server
{
try
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
return;
@ -85,7 +87,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
@ -93,16 +95,16 @@ namespace RageCoop.Server
{
try
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
Logging.Error($"[Client->SendNativeCall(ulong hash, params object[] args)]: Connection \"{ClientID}\" not found!");
Program.Logger.Error($"[Client->SendNativeCall(ulong hash, params object[] args)]: Connection \"{NetID}\" not found!");
return;
}
if (args != null && args.Length == 0)
{
Logging.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing arguments!");
Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing arguments!");
return;
}
@ -118,7 +120,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
@ -126,16 +128,16 @@ namespace RageCoop.Server
{
try
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
Logging.Error($"[Client->SendNativeResponse(Action<object> callback, ulong hash, Type type, params object[] args)]: Connection \"{ClientID}\" not found!");
Program.Logger.Error($"[Client->SendNativeResponse(Action<object> callback, ulong hash, Type type, params object[] args)]: Connection \"{NetID}\" not found!");
return;
}
if (args != null && args.Length == 0)
{
Logging.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing arguments!");
Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing arguments!");
return;
}
@ -159,13 +161,13 @@ namespace RageCoop.Server
{
returnTypeValue = 0x03;
}
else if (returnType == typeof(LVector3))
else if (returnType == typeof(Vector3))
{
returnTypeValue = 0x04;
}
else
{
Logging.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing return type!");
Program.Logger.Error($"[Client->SendNativeCall(ulong hash, Dictionary<string, object> args)]: Missing return type!");
return;
}
@ -181,16 +183,16 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
public void SendCleanUpWorld()
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
Logging.Error($"[Client->SendCleanUpWorld()]: Connection \"{ClientID}\" not found!");
Program.Logger.Error($"[Client->SendCleanUpWorld()]: Connection \"{NetID}\" not found!");
return;
}
@ -203,7 +205,7 @@ namespace RageCoop.Server
{
try
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
return;
@ -212,8 +214,6 @@ namespace RageCoop.Server
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.Mod()
{
NetHandle = 0,
Target = 0,
Name = modName,
CustomPacketID = customID,
Bytes = bytes
@ -223,7 +223,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
@ -231,13 +231,13 @@ namespace RageCoop.Server
{
if (!FilesReceived)
{
Logging.Warning($"Player \"{Player.Username}\" doesn't have all the files yet!");
Program.Logger.Warning($"Player \"{Player.Username}\" doesn't have all the files yet!");
return;
}
try
{
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ClientID);
NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == NetID);
if (userConnection == null)
{
return;
@ -254,7 +254,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
#endregion

View File

@ -43,7 +43,7 @@ namespace RageCoop.Server
// ONLY JAVASCRIPT AND JSON FILES!
if (!new string[] { ".js", ".xml" }.Any(x => x == fileInfo.Extension))
{
Logging.Warning("Only files with \"*.js\" and \"*.xml\" can be sent!");
Program.Logger.Warning("Only files with \"*.js\" and \"*.xml\" can be sent!");
continue;
}
@ -105,7 +105,7 @@ namespace RageCoop.Server
{
lock (Server.Clients)
{
Client x = Server.Clients.FirstOrDefault(x => x.ClientID == client.NetHandle);
Client x = Util.GetClientByID(client.NetHandle);
if (x != null)
{
x.FilesReceived = true;

View File

@ -1,79 +0,0 @@
using System;
using System.IO;
namespace RageCoop.Server
{
public class Logging
{
private static readonly object _lock = new();
public static void Info(string message)
{
lock (_lock)
{
string msg = string.Format("[{0}] [INFO] {1}", Date(), message);
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine(msg);
Console.ResetColor();
using StreamWriter sw = new("log.txt", true);
sw.WriteLine(msg);
}
}
public static void Warning(string message)
{
lock (_lock)
{
string msg = string.Format("[{0}] [WARNING] {1}", Date(), message);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(msg);
Console.ResetColor();
using StreamWriter sw = new("log.txt", true);
sw.WriteLine(msg);
}
}
public static void Error(string message)
{
lock (_lock)
{
string msg = string.Format("[{0}] [ERROR] {1}", Date(), message);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(msg);
Console.ResetColor();
using StreamWriter sw = new("log.txt", true);
sw.WriteLine(msg);
}
}
public static void Debug(string message)
{
if (!Server.MainSettings.DebugMode)
{
return;
}
lock (_lock)
{
string msg = string.Format("[{0}] [DEBUG] {1}", Date(), message);
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine(msg);
Console.ResetColor();
using StreamWriter sw = new("log.txt", true);
sw.WriteLine(msg);
}
}
private static string Date()
{
return DateTime.Now.ToString();
}
}
}

View File

@ -9,8 +9,14 @@ namespace RageCoop.Server
class Program
{
public static bool ReadyToStop = false;
public static Core.Logging.Logger Logger;
static void Main(string[] args)
{
Logger=new Core.Logging.Logger()
{
LogPath="RageCoop.Server.log",
UseConsole=true,
};
try
{
#if DEBUG
@ -45,7 +51,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error(e.InnerException?.Message ?? e.Message);
Program.Logger.Error(e.InnerException?.Message ?? e.Message);
Console.ReadLine();
}
}

View File

@ -6,10 +6,10 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
<PropertyGroup>
<Configuration>Release</Configuration>
<Platform>Any CPU</Platform>
<PublishDir>bin\Release\net6.0\publish\osx-x64\</PublishDir>
<PublishDir>bin\Release\net6.0\publish\linux-arm\</PublishDir>
<PublishProtocol>FileSystem</PublishProtocol>
<TargetFramework>net6.0</TargetFramework>
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
<RuntimeIdentifier>linux-arm</RuntimeIdentifier>
<SelfContained>true</SelfContained>
<PublishSingleFile>True</PublishSingleFile>
<PublishTrimmed>True</PublishTrimmed>

View File

@ -4,6 +4,6 @@ https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<History>True|2022-05-25T02:30:00.0927959Z;True|2022-05-25T10:26:50.6739643+08:00;True|2022-05-25T10:20:36.6658425+08:00;True|2022-05-25T10:19:47.8333108+08:00;True|2022-05-24T11:00:13.3617113+08:00;True|2022-05-22T16:56:31.0481188+08:00;True|2022-05-18T13:35:57.1402751+08:00;True|2022-05-18T13:10:28.4995253+08:00;True|2022-05-01T18:35:01.9624101+08:00;True|2022-05-01T12:32:20.8671319+08:00;False|2022-05-01T12:30:25.4596227+08:00;</History>
<History>True|2022-06-02T05:21:10.3456459Z;True|2022-06-02T13:20:52.1088278+08:00;True|2022-06-02T13:20:25.6889167+08:00;True|2022-06-02T13:19:06.3089340+08:00;True|2022-06-01T18:47:39.6707493+08:00;True|2022-06-01T18:04:32.2932367+08:00;True|2022-06-01T18:03:17.8871227+08:00;True|2022-05-27T15:20:25.7264350+08:00;True|2022-05-27T15:20:04.2362276+08:00;True|2022-05-27T15:19:21.4852644+08:00;True|2022-05-27T15:18:36.0857345+08:00;True|2022-05-25T10:30:00.0927959+08:00;True|2022-05-25T10:26:50.6739643+08:00;True|2022-05-25T10:20:36.6658425+08:00;True|2022-05-25T10:19:47.8333108+08:00;True|2022-05-24T11:00:13.3617113+08:00;True|2022-05-22T16:56:31.0481188+08:00;True|2022-05-18T13:35:57.1402751+08:00;True|2022-05-18T13:10:28.4995253+08:00;True|2022-05-01T18:35:01.9624101+08:00;True|2022-05-01T12:32:20.8671319+08:00;False|2022-05-01T12:30:25.4596227+08:00;</History>
</PropertyGroup>
</Project>

View File

@ -3,8 +3,8 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<AssemblyVersion>0.2</AssemblyVersion>
<FileVersion>0.2</FileVersion>
<AssemblyVersion>0.4</AssemblyVersion>
<FileVersion>0.4</FileVersion>
<RepositoryUrl>https://github.com/RAGECOOP/RAGECOOP-V</RepositoryUrl>
<PackageProjectUrl>https://ragecoop.online/</PackageProjectUrl>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
@ -12,7 +12,7 @@
<Product>$(AssemblyName)-V</Product>
<PackageId>RAGECOOP-V</PackageId>
<Authors>RAGECOOP</Authors>
<Version>0.2</Version>
<Version>0.4</Version>
</PropertyGroup>
<ItemGroup>
@ -32,6 +32,9 @@
<Reference Include="Newtonsoft.Json">
<HintPath>..\Libs\Release\scripts\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="ScriptHookVDotNet3">
<HintPath>..\Libs\Release\ScriptHookVDotNet3.dll</HintPath>
</Reference>
</ItemGroup>

View File

@ -11,6 +11,7 @@ using RageCoop.Core;
using Newtonsoft.Json;
using System.Threading.Tasks;
using Lidgren.Network;
using System.Timers;
namespace RageCoop.Server
{
@ -22,29 +23,26 @@ namespace RageCoop.Server
public class Server
{
private static readonly string _compatibleVersion = "V0_2";
private static readonly string _compatibleVersion = "V0_4";
private static long _currentTick = 0;
public static readonly Settings MainSettings = Util.Read<Settings>("Settings.xml");
private readonly Blocklist _mainBlocklist = Util.Read<Blocklist>("Blocklist.xml");
private readonly Allowlist _mainAllowlist = Util.Read<Allowlist>("Allowlist.xml");
public static NetServer MainNetServer;
public static Resource RunningResource = null;
public static readonly Dictionary<Command, Action<CommandContext>> Commands = new();
public static readonly Dictionary<TriggerEvent, Action<EventContext>> TriggerEvents = new();
private static Thread BackgroundThread;
public static readonly List<Client> Clients = new();
public static readonly Dictionary<long,Client> Clients = new();
private static System.Timers.Timer SendLatencyTimer = new System.Timers.Timer(5000);
public Server()
{
Logging.Info("================");
Logging.Info($"Server bound to: 0.0.0.0:{MainSettings.Port}");
Logging.Info($"Server version: {Assembly.GetCallingAssembly().GetName().Version}");
Logging.Info($"Compatible RAGECOOP versions: {_compatibleVersion.Replace('_', '.')}.x");
Logging.Info("================");
Program.Logger.Info("================");
Program.Logger.Info($"Server bound to: 0.0.0.0:{MainSettings.Port}");
Program.Logger.Info($"Server version: {Assembly.GetCallingAssembly().GetName().Version}");
Program.Logger.Info($"Compatible RAGECOOP versions: {_compatibleVersion.Replace('_', '.')}.x");
Program.Logger.Info("================");
// 623c92c287cc392406e7aaaac1c0f3b0 = RAGECOOP
NetPeerConfiguration config = new("623c92c287cc392406e7aaaac1c0f3b0")
@ -59,27 +57,28 @@ namespace RageCoop.Server
MainNetServer = new NetServer(config);
MainNetServer.Start();
Logging.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port));
SendLatencyTimer.Elapsed+=((s,e) => { SendLatency(); });
SendLatencyTimer.AutoReset=true;
SendLatencyTimer.Enabled=true;
Program.Logger.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port));
if (MainSettings.UPnP)
{
Logging.Info(string.Format("Attempting to forward port {0}", MainSettings.Port));
Program.Logger.Info(string.Format("Attempting to forward port {0}", MainSettings.Port));
if (MainNetServer.UPnP.ForwardPort(MainSettings.Port, "RAGECOOP server"))
{
Logging.Info(string.Format("Server available on {0}:{1}", MainNetServer.UPnP.GetExternalIP().ToString(), config.Port));
Program.Logger.Info(string.Format("Server available on {0}:{1}", MainNetServer.UPnP.GetExternalIP().ToString(), config.Port));
}
else
{
Logging.Error("Port forwarding failed! Your router may not support UPnP.");
Logging.Warning("If you and your friends can join this server, please ignore this error or set UPnP in Settings.xml to false!");
Program.Logger.Error("Port forwarding failed! Your router may not support UPnP.");
Program.Logger.Warning("If you and your friends can join this server, please ignore this error or set UPnP in Settings.xml to false!");
}
}
if (MainSettings.AnnounceSelf)
{
Logging.Info("Announcing to master server...");
#region -- MASTERSERVER --
new Thread(async () =>
@ -104,13 +103,14 @@ namespace RageCoop.Server
string content = await response.Content.ReadAsStringAsync();
info = JsonConvert.DeserializeObject<IpInfo>(content);
Program.Logger.Info($"Your public IP is {info.Address}, announcing to master server...");
}
catch (Exception ex)
{
Logging.Error(ex.InnerException?.Message ?? ex.Message);
Program.Logger.Error(ex.InnerException?.Message ?? ex.Message);
return;
}
var realMaster = MainSettings.MasterServer=="[AUTO]" ? Util.DownloadString("https://ragecoop.online/stuff/masterserver") : MainSettings.MasterServer;
while (!Program.ReadyToStop)
{
string msg =
@ -120,20 +120,16 @@ namespace RageCoop.Server
"\"name\": \"" + MainSettings.Name + "\", " +
"\"version\": \"" + _compatibleVersion.Replace("_", ".") + "\", " +
"\"players\": \"" + MainNetServer.ConnectionsCount + "\", " +
"\"maxPlayers\": \"" + MainSettings.MaxPlayers + "\", " +
"\"allowlist\": \"" + _mainAllowlist.Username.Any() + "\", " +
"\"mods\": \"" + MainSettings.ModsAllowed + "\", " +
"\"npcs\": \"" + MainSettings.NpcsAllowed + "\"" +
"\"maxPlayers\": \"" + MainSettings.MaxPlayers + "\"" +
" }";
HttpResponseMessage response = null;
try
{
response = await httpClient.PostAsync(MainSettings.MasterServer, new StringContent(msg, Encoding.UTF8, "application/json"));
response = await httpClient.PostAsync(realMaster, new StringContent(msg, Encoding.UTF8, "application/json"));
}
catch (Exception ex)
{
Logging.Error($"MasterServer: {ex.Message}");
Program.Logger.Error($"MasterServer: {ex.Message}");
// Sleep for 5s
Thread.Sleep(5000);
@ -142,18 +138,19 @@ namespace RageCoop.Server
if (response == null)
{
Logging.Error("MasterServer: Something went wrong!");
Program.Logger.Error("MasterServer: Something went wrong!");
}
else if (response.StatusCode != HttpStatusCode.OK)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
string requestContent = await response.Content.ReadAsStringAsync();
Logging.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
Program.Logger.Error($"MasterServer: [{(int)response.StatusCode}], {requestContent}");
}
else
{
Logging.Error($"MasterServer: [{(int)response.StatusCode}]");
Program.Logger.Error($"MasterServer: [{(int)response.StatusCode}]");
Program.Logger.Error($"MasterServer: [{await response.Content.ReadAsStringAsync()}]");
}
}
@ -163,11 +160,11 @@ namespace RageCoop.Server
}
catch (HttpRequestException ex)
{
Logging.Error($"MasterServer: {ex.InnerException.Message}");
Program.Logger.Error($"MasterServer: {ex.InnerException.Message}");
}
catch (Exception ex)
{
Logging.Error($"MasterServer: {ex.Message}");
Program.Logger.Error($"MasterServer: {ex.Message}");
}
}).Start();
#endregion
@ -178,7 +175,7 @@ namespace RageCoop.Server
try
{
string resourcepath = AppDomain.CurrentDomain.BaseDirectory + "resources" + Path.DirectorySeparatorChar + MainSettings.Resource + ".dll";
Logging.Info($"Loading resource \"{MainSettings.Resource}.dll\"...");
Program.Logger.Info($"Loading resource \"{MainSettings.Resource}.dll\"...");
Assembly asm = Assembly.LoadFrom(resourcepath);
Type[] types = asm.GetExportedTypes();
@ -187,7 +184,7 @@ namespace RageCoop.Server
if (!enumerable.Any())
{
Logging.Error("ERROR: No classes that inherit from ServerScript have been found in the assembly. Starting freeroam.");
Program.Logger.Error("ERROR: No classes that inherit from ServerScript have been found in the assembly. Starting freeroam.");
}
else
{
@ -197,28 +194,27 @@ namespace RageCoop.Server
}
else
{
Logging.Warning("Could not create resource: it is null.");
Program.Logger.Warning("Could not create resource: it is null.");
}
}
}
catch (Exception e)
{
Logging.Error(e.InnerException.Message);
Program.Logger.Error(e.InnerException.Message);
}
}
Logging.Info("Searching for client-side files...");
Program.Logger.Info("Searching for client-side files...");
DownloadManager.CheckForDirectoryAndFiles();
Listen();
BackgroundThread=new Thread(() => Background());
Listen();
}
private void Listen()
{
Logging.Info("Listening for clients");
Logging.Info("Please use CTRL + C if you want to stop the server!");
Program.Logger.Info("Listening for clients");
Program.Logger.Info("Please use CTRL + C if you want to stop the server!");
while (!Program.ReadyToStop)
{
@ -232,11 +228,11 @@ namespace RageCoop.Server
{
lock (Clients)
{
Clients.ForEach(client =>
Clients.Values.ToList().ForEach(client =>
{
if (!client.FilesSent)
{
DownloadManager.InsertClient(client.ClientID);
DownloadManager.InsertClient(client.NetID);
client.FilesSent = true;
}
});
@ -253,10 +249,10 @@ namespace RageCoop.Server
{
case NetIncomingMessageType.ConnectionApproval:
{
Logging.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
Program.Logger.Info($"New incoming connection from: [{message.SenderConnection.RemoteEndPoint}]");
if (message.ReadByte() != (byte)PacketTypes.Handshake)
{
Logging.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
Program.Logger.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: Wrong packet!");
message.SenderConnection.Deny("Wrong packet!");
}
else
@ -273,7 +269,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
Program.Logger.Info($"IP [{message.SenderConnection.RemoteEndPoint.Address}] was blocked, reason: {e.Message}");
message.SenderConnection.Deny(e.Message);
}
}
@ -317,7 +313,7 @@ namespace RageCoop.Server
Packets.PedStateSync packet = new();
packet.Unpack(data);
CharacterStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
PedStateSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
@ -353,7 +349,7 @@ namespace RageCoop.Server
Packets.PedSync packet = new();
packet.Unpack(data);
CharacterSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
PedSync(packet, message.SenderConnection.RemoteUniqueIdentifier);
}
catch (Exception e)
{
@ -429,7 +425,7 @@ namespace RageCoop.Server
Packets.NativeResponse packet = new();
packet.Unpack(data);
Client client = Clients.Find(x => x.ClientID == message.SenderConnection.RemoteUniqueIdentifier);
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
if (client != null)
{
if (client.Callbacks.ContainsKey(packet.ID))
@ -445,63 +441,6 @@ namespace RageCoop.Server
}
}
break;
case PacketTypes.Mod:
{
if (MainSettings.ModsAllowed)
{
try
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.Mod packet = new Packets.Mod();
packet.Unpack(data);
bool resourceResult = false;
if (RunningResource != null)
{
if (RunningResource.InvokeModPacketReceived(packet.NetHandle, packet.Target, packet.Name, packet.CustomPacketID, packet.Bytes))
{
resourceResult = true;
}
}
if (!resourceResult && packet.Target != -1)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
if (packet.Target != 0)
{
NetConnection target = MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == packet.Target);
if (target == null)
{
Logging.Error($"[ModPacket] target \"{packet.Target}\" not found!");
}
else
{
// Send back to target
MainNetServer.SendMessage(outgoingMessage, target, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
}
}
else
{
// Send back to all players
MainNetServer.SendMessage(outgoingMessage, MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
}
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
else
{
message.SenderConnection.Disconnect("Mods are not allowed!");
}
}
break;
case PacketTypes.FileTransferComplete:
{
try
@ -514,10 +453,10 @@ namespace RageCoop.Server
Packets.FileTransferComplete packet = new();
packet.Unpack(data);
Client client = Clients.Find(x => x.ClientID == message.SenderConnection.RemoteUniqueIdentifier);
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
if (client != null && !client.FilesReceived)
{
DownloadManager.TryToRemoveClient(client.ClientID, packet.ID);
DownloadManager.TryToRemoveClient(client.NetID, packet.ID);
}
}
}
@ -558,7 +497,7 @@ namespace RageCoop.Server
}
else
{
Logging.Warning($"Player \"{client.Player.Username}\" attempted to trigger an unknown event! [{packet.EventName}]");
Program.Logger.Warning($"Player \"{client.Player.Username}\" attempted to trigger an unknown event! [{packet.EventName}]");
}
}
}
@ -594,14 +533,14 @@ namespace RageCoop.Server
}
else
{
Logging.Error("Unhandled Data / Packet type");
Program.Logger.Error("Unhandled Data / Packet type");
}
break;
}
break;
case NetIncomingMessageType.ConnectionLatencyUpdated:
{
Client client = Clients.Find(x => x.ClientID == message.SenderConnection.RemoteUniqueIdentifier);
Client client = Util.GetClientByID(message.SenderConnection.RemoteUniqueIdentifier);
if (client != null)
{
client.Latency = message.ReadFloat();
@ -609,28 +548,28 @@ namespace RageCoop.Server
}
break;
case NetIncomingMessageType.ErrorMessage:
Logging.Error(message.ReadString());
Program.Logger.Error(message.ReadString());
break;
case NetIncomingMessageType.WarningMessage:
Logging.Warning(message.ReadString());
Program.Logger.Warning(message.ReadString());
break;
case NetIncomingMessageType.DebugMessage:
case NetIncomingMessageType.VerboseDebugMessage:
Logging.Debug(message.ReadString());
Program.Logger.Debug(message.ReadString());
break;
default:
Logging.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
Program.Logger.Error(string.Format("Unhandled type: {0} {1} bytes {2} | {3}", message.MessageType, message.LengthBytes, message.DeliveryMethod, message.SequenceChannel));
break;
}
MainNetServer.Recycle(message);
}
// 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60);
// 3 milliseconds to sleep to reduce CPU usage
Thread.Sleep(3);
}
Logging.Warning("Server is shutting down!");
Program.Logger.Warning("Server is shutting down!");
if (RunningResource != null)
{
// Waiting for resource...
@ -648,36 +587,32 @@ namespace RageCoop.Server
// Sleep for 1 second
Thread.Sleep(1000);
}
Program.Logger.Dispose();
}
private void Background()
private void SendLatency()
{
while (true)
foreach (Client c in Clients.Values)
{
foreach(Client c in Clients)
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != c.NetID).ForEach(x =>
{
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != c.ClientID).ForEach(x =>
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
new Packets.PlayerInfoUpdate()
{
PedID=c.Player.PedID,
Username=c.Player.Username,
Latency=c.Player.Latency=c.Latency,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
});
}
// Update Latency every 20 seconds.
Thread.Sleep(1000*20);
PedID=c.Player.PedID,
Username=c.Player.Username,
Latency=c.Player.Latency=c.Latency,
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.ReliableSequenced, (byte)ConnectionChannel.Default);
});
}
}
private void DisconnectAndLog(NetConnection senderConnection,PacketTypes type, Exception e)
{
Logging.Error($"Error receiving a packet of type {type}");
Logging.Error(e.Message);
Logging.Error(e.StackTrace);
Program.Logger.Error($"Error receiving a packet of type {type}");
Program.Logger.Error(e.Message);
Program.Logger.Error(e.StackTrace);
senderConnection.Disconnect(e.Message);
}
@ -685,7 +620,7 @@ namespace RageCoop.Server
// Before we approve the connection, we must shake hands
private void GetHandshake(NetConnection local, Packets.Handshake packet)
{
Logging.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + local.RemoteEndPoint.Address.ToString() + "]");
Program.Logger.Debug("New handshake from: [Name: " + packet.Username + " | Address: " + local.RemoteEndPoint.Address.ToString() + "]");
if (!packet.ModVersion.StartsWith(_compatibleVersion))
{
@ -702,11 +637,6 @@ namespace RageCoop.Server
local.Deny("Username contains special chars!");
return;
}
if (_mainAllowlist.Username.Any() && !_mainAllowlist.Username.Contains(packet.Username.ToLower()))
{
local.Deny("This Username is not on the allow list!");
return;
}
if (_mainBlocklist.Username.Contains(packet.Username.ToLower()))
{
local.Deny("This Username has been blocked by this server!");
@ -717,7 +647,7 @@ namespace RageCoop.Server
local.Deny("This IP was blocked by this server!");
return;
}
if (Clients.Any(x => x.Player.Username.ToLower() == packet.Username.ToLower()))
if (Clients.Values.Any(x => x.Player.Username.ToLower() == packet.Username.ToLower()))
{
local.Deny("Username is already taken!");
return;
@ -730,19 +660,20 @@ namespace RageCoop.Server
// Add the player to Players
lock (Clients)
{
Clients.Add(
Clients.Add(local.RemoteUniqueIdentifier,
tmpClient = new Client()
{
ClientID = local.RemoteUniqueIdentifier,
NetID = local.RemoteUniqueIdentifier,
Connection=local,
Player = new()
{
Username = packet.Username,
PedID=packet.PedID
PedID=packet.PedID,
}
}
);;
}
Logging.Info($"HandShake sucess, Player:{packet.Username} PedID:{packet.PedID}");
Program.Logger.Info($"Handshake sucess, Player:{packet.Username} PedID:{packet.PedID}");
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
// Create a new handshake packet
@ -751,7 +682,6 @@ namespace RageCoop.Server
PedID = packet.PedID,
Username = string.Empty,
ModVersion = string.Empty,
NPCsAllowed = MainSettings.NpcsAllowed
}.Pack(outgoingMessage);
// Accept the connection and send back a new handshake packet with the connection ID
@ -766,7 +696,7 @@ namespace RageCoop.Server
// The connection has been approved, now we need to send all other players to the new player and the new player to all players
private static void SendPlayerConnectPacket(NetConnection local)
{
Client localClient = Clients.Find(x => x.ClientID == local.RemoteUniqueIdentifier);
Client localClient = Util.GetClientByID(local.RemoteUniqueIdentifier);
if (localClient == null)
{
local.Disconnect("No data found!");
@ -781,7 +711,7 @@ namespace RageCoop.Server
{
long targetNetHandle = targetPlayer.RemoteUniqueIdentifier;
Client targetClient = Clients.Find(x => x.ClientID == targetNetHandle);
Client targetClient = Util.GetClientByID(targetNetHandle);
if (targetClient != null)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
@ -813,7 +743,7 @@ namespace RageCoop.Server
}
else
{
Logging.Info($"Player {localClient.Player.Username} connected!");
Program.Logger.Info($"Player {localClient.Player.Username} connected!");
}
if (!string.IsNullOrEmpty(MainSettings.WelcomeMessage))
@ -826,7 +756,7 @@ namespace RageCoop.Server
private static void SendPlayerDisconnectPacket(long nethandle)
{
List<NetConnection> clients = MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != nethandle);
int playerPedID = Clients.Where(x => x.ClientID==nethandle).First().Player.PedID;
int playerPedID = Clients[nethandle].Player.PedID;
if (clients.Count > 0)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
@ -838,13 +768,13 @@ namespace RageCoop.Server
MainNetServer.SendMessage(outgoingMessage, clients, NetDeliveryMethod.ReliableOrdered, 0);
}
Client localClient = Clients.FirstOrDefault(x => x.ClientID == nethandle);
Client localClient = Util.GetClientByID( nethandle);
if (localClient == null)
{
return;
}
Clients.Remove(localClient);
Clients.Remove(localClient.NetID);
if (RunningResource != null)
{
@ -852,12 +782,12 @@ namespace RageCoop.Server
}
else
{
Logging.Info($"Player {localClient.Player.Username} disconnected! ID:{playerPedID}");
Program.Logger.Info($"Player {localClient.Player.Username} disconnected! ID:{playerPedID}");
}
}
#region SyncEntities
private static void CharacterStateSync(Packets.PedStateSync packet,long ClientID)
private static void PedStateSync(Packets.PedStateSync packet,long ClientID)
{
@ -868,15 +798,13 @@ namespace RageCoop.Server
}
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
foreach (var c in Clients.Values)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
});
if (c.NetID==client.NetID) { continue; }
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
if (RunningResource != null && packet.ID==client.Player.PedID)
{
RunningResource.InvokePlayerUpdate(client);
@ -896,33 +824,47 @@ namespace RageCoop.Server
client.Player.VehicleID = packet.ID;
}
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
foreach (var c in Clients.Values)
{
if (c.NetID==client.NetID) { continue; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.VehicleSync);
});
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
private static void CharacterSync(Packets.PedSync packet, long ClientID)
private static void PedSync(Packets.PedSync packet, long ClientID)
{
Client client = Util.GetClientByID(ClientID);
if (client == null)
{
return;
}
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
bool isPlayer = packet.ID==client.Player.PedID;
if (isPlayer) { client.Player.Position=packet.Position; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
foreach (var c in Clients.Values)
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
});
// Don't send data back
if (c.NetID==client.NetID) { continue; }
if (RunningResource != null && packet.ID==client.Player.PedID)
// Check streaming distance
if (isPlayer)
{
if ((MainSettings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>MainSettings.PlayerStreamingDistance))
{
continue;
}
}
else if ((MainSettings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>MainSettings.NpcStreamingDistance))
{
continue;
}
MainNetServer.SendMessage(outgoingMessage,c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
if (RunningResource != null && isPlayer)
{
RunningResource.InvokePlayerUpdate(client);
}
@ -934,43 +876,27 @@ namespace RageCoop.Server
{
return;
}
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
bool isPlayer = packet.ID==client.Player.VehicleID;
foreach (var c in Clients.Values)
{
if (c.NetID==client.NetID) { continue; }
if (isPlayer)
{
// Player's vehicle
if ((MainSettings.PlayerStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>MainSettings.PlayerStreamingDistance))
{
continue;
}
}
else if((MainSettings.NpcStreamingDistance!=-1)&&(packet.Position.DistanceTo(c.Player.Position)>MainSettings.NpcStreamingDistance))
{
continue;
}
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.VehicleSync);
});
/*
Client client = Util.GetClientByID(ClientID);
if (client == null)
{
return;
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
// Save the new data
client.Player.PedHandle = packet.PedHandle;
client.Player.VehicleHandle = packet.VehicleHandle;
client.Player.IsInVehicle = true;
client.Player.Position = packet.Position;
client.Player.Health = packet.Health;
// Override the latency
packet.Latency = client.Latency;
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
{
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PlayerFull);
});
if (RunningResource != null)
{
RunningResource.InvokePlayerUpdate(client);
}
*/
}
private static void ProjectileSync(Packets.ProjectileSync packet, long ClientID)
{
@ -980,12 +906,13 @@ namespace RageCoop.Server
return;
}
MainNetServer.Connections.FindAll(x => x.RemoteUniqueIdentifier != ClientID).ForEach(x =>
foreach (var c in Clients.Values)
{
if (c.NetID==client.NetID) { continue; }
NetOutgoingMessage outgoingMessage = MainNetServer.CreateMessage();
packet.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage, x, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.ProjectileSync);
});
MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.UnreliableSequenced, (byte)ConnectionChannel.PedSync);
}
}
#endregion
@ -1004,7 +931,7 @@ namespace RageCoop.Server
CommandContext ctx = new()
{
Client = Clients.Find(x => x.Player.Username == packet.Username),
Client = Clients.Values.Where(x => x.Player.Username == packet.Username).FirstOrDefault(),
Args = argsWithoutCmd
};
@ -1048,7 +975,7 @@ namespace RageCoop.Server
SendChatMessage(packet.Username, packet.Message, targets);
Logging.Info(packet.Username + ": " + packet.Message);
Program.Logger.Info(packet.Username + ": " + packet.Message);
}
public static void SendChatMessage(string username, string message, List<NetConnection> targets = null)

View File

@ -55,8 +55,8 @@ namespace RageCoop.Server
(localQueue.Dequeue() as Action)?.Invoke();
}
// 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60);
// 15 milliseconds to sleep to reduce CPU usage
Thread.Sleep(15);
}
_script.API.InvokeStop();
@ -105,13 +105,6 @@ namespace RageCoop.Server
return task.Result;
}
public void InvokePlayerPositionUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerPositionUpdate(username)));
}
}
public void InvokePlayerUpdate(Client client)
{
@ -121,29 +114,6 @@ namespace RageCoop.Server
}
}
public void InvokePlayerHealthUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerHealthUpdate(username)));
}
}
public void InvokePlayerPedHandleUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerPedHandleUpdate(username)));
}
}
public void InvokePlayerVehicleHandleUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerVehicleHandleUpdate(username)));
}
}
public void InvokeTick(long tick)
{
@ -197,25 +167,6 @@ namespace RageCoop.Server
/// Called when a new player sends data like health
/// </summary>
public event PlayerEvent OnPlayerUpdate;
/// <summary>
/// Called when a player has a new health value
/// </summary>
public event PlayerEvent OnPlayerHealthUpdate;
/// <summary>
/// Called when a player has a new position
/// </summary>
public event PlayerEvent OnPlayerPositionUpdate;
/// <summary>
/// Called when a player has a new position
/// </summary>
public event PlayerEvent OnPlayerPedHandleUpdate;
/// <summary>
/// Called when a player has a new position
/// </summary>
public event PlayerEvent OnPlayerVehicleHandleUpdate;
/// <summary>
/// Called when a player sends a packet from another modification
/// </summary>
public event ModEvent OnModPacketReceived;
public void InvokeTick(long tick)
@ -253,11 +204,6 @@ namespace RageCoop.Server
OnPlayerUpdate?.Invoke(client);
}
public void InvokePlayerHealthUpdate(string username)
{
OnPlayerHealthUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
public bool InvokeChatMessage(string username, string message)
{
CancelEventArgs args = new(false);
@ -265,21 +211,6 @@ namespace RageCoop.Server
return args.Cancel;
}
public void InvokePlayerPositionUpdate(string username)
{
OnPlayerPositionUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
public void InvokePlayerPedHandleUpdate(string username)
{
OnPlayerPedHandleUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
public void InvokePlayerVehicleHandleUpdate(string username)
{
OnPlayerVehicleHandleUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
public bool InvokeModPacketReceived(long from, long target, string modName, byte customID, byte[] bytes)
{
CancelEventArgs args = new(false);
@ -295,35 +226,39 @@ namespace RageCoop.Server
/// <param name="modName">The name of the modification that will receive the data</param>
/// <param name="customID">The ID to check what this data is</param>
/// <param name="bytes">The serialized data</param>
/// <param name="netHandleList">The list of connections (players) that will receive the data</param>
public static void SendModPacketToAll(string modName, byte customID, byte[] bytes, List<long> netHandleList = null)
/// <param name="playerList">The list of player ID (PedID) that will receive the data</param>
public static void SendModPacketToAll(string modName, byte customID, byte[] bytes, List<int> playerList = null)
{
try
{
List<NetConnection> connections = netHandleList == null
? Server.MainNetServer.Connections
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
// A resource can be calling this function on disconnect of the last player in the server and we will
{// A resource can be calling this function on disconnect of the last player in the server and we will
// get an empty connection list, make sure connections has at least one handle in it
if (connections.Count > 0)
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.Mod()
{
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.Mod()
{
NetHandle = 0,
Target = 0,
Name = modName,
CustomPacketID = customID,
Bytes = bytes
}.Pack(outgoingMessage);
Logging.Debug($"SendModPacketToAll recipients list {connections.Count}");
Server.MainNetServer.SendMessage(outgoingMessage, connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
Server.MainNetServer.FlushSendQueue();
Name = modName,
CustomPacketID = customID,
Bytes = bytes
}.Pack(outgoingMessage);
if (playerList==null)
{
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
}
else
{
foreach(var c in Server.Clients.Values)
{
if (playerList.Contains(c.Player.PedID))
{
Server.MainNetServer.SendMessage(outgoingMessage, c.Connection, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
}
}
}
Server.MainNetServer.FlushSendQueue();
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
@ -344,7 +279,7 @@ namespace RageCoop.Server
if (args != null && args.Length == 0)
{
Logging.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!");
Program.Logger.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!");
return;
}
@ -360,37 +295,15 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}
/// <summary>
/// Get all connections as a list of NetHandle(long)
/// </summary>
/// <returns>All connections(NetHandle) as a List</returns>
public static List<long> GetAllConnections()
{
List<long> result = new();
Server.MainNetServer.Connections.ForEach(x => result.Add(x.RemoteUniqueIdentifier));
return result;
}
/// <summary>
/// Get the count of all connections
/// </summary>
/// <returns>The count of all connections as an integer</returns>
public static int GetAllClientsCount()
{
return Server.Clients.Count;
}
/// <summary>
/// Get a list of all Clients
/// </summary>
/// <returns>All Clients as a List</returns>
public static List<Client> GetAllClients()
/// <returns>All clients as a dictionary indexed by NetID</returns>
public static Dictionary<long,Client> GetAllClients()
{
return Server.Clients;
}
@ -402,7 +315,7 @@ namespace RageCoop.Server
/// <returns>The Client from this user or null</returns>
public static Client GetClientByUsername(string username)
{
return Server.Clients.FirstOrDefault(x => x.Player.Username.ToLower() == username.ToLower());
return Server.Clients.Values.FirstOrDefault(x => x.Player.Username.ToLower() == username.ToLower());
}
/// <summary>
@ -428,7 +341,7 @@ namespace RageCoop.Server
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
Program.Logger.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
}

View File

@ -3,16 +3,22 @@
public class Settings
{
public int Port { get; set; } = 4499;
public int MaxPlayers { get; set; } = 16;
public int MaxLatency { get; set; } = 300;
public int MaxPlayers { get; set; } = 32;
public int MaxLatency { get; set; } = 500;
public string Name { get; set; } = "RAGECOOP server";
public string WelcomeMessage { get; set; } = "Welcome on this server :)";
public string Resource { get; set; } = "";
public bool NpcsAllowed { get; set; } = true;
public bool ModsAllowed { get; set; } = false;
public bool UPnP { get; set; } = true;
public bool AnnounceSelf { get; set; } = false;
public string MasterServer { get; set; } = "https://ragecoop.online/gtav/servers";
public string MasterServer { get; set; } = "[AUTO]";
public bool DebugMode { get; set; } = false;
/// <summary>
/// NPC data won't be sent to a player if their distance is greater than this value. -1 for unlimited.
/// </summary>
public float NpcStreamingDistance { get; set; } = 1000;
/// <summary>
/// Player's data won't be sent to another player if their distance is greater than this value. -1 for unlimited.
/// </summary>
public float PlayerStreamingDistance { get; set; } = -1;
}
}

View File

@ -6,11 +6,30 @@ using System.Linq;
using System.Collections.Generic;
using RageCoop.Core;
using Lidgren.Network;
using System.Net;
namespace RageCoop.Server
{
static partial class Util
{
public static string DownloadString(string url)
{
try
{
// TLS only
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient client = new();
return client.DownloadString(url);
}
catch
{
return "";
}
}
public static (byte, byte[]) GetBytesFromObject(object obj)
{
return obj switch
@ -30,7 +49,8 @@ namespace RageCoop.Server
public static Client GetClientByID(long id)
{
Client result = Server.Clients.Find(x => x.ClientID == id);
Client result = null;
Server.Clients.TryGetValue(id,out result);
if (result == null)
{
NetConnection localConn = Server.MainNetServer.Connections.Find(x => id == x.RemoteUniqueIdentifier);
@ -46,13 +66,13 @@ namespace RageCoop.Server
public static NetConnection GetConnectionByUsername(string username)
{
Client client = Server.Clients.Find(x => x.Player.Username.ToLower() == username.ToLower());
Client client = Server.Clients.Values.ToList().Find(x => x.Player.Username.ToLower() == username.ToLower());
if (client == null)
{
return null;
}
return Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == client.ClientID);
return Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == client.NetID);
}
// Return a list of all connections but not the local connection
@ -65,25 +85,6 @@ namespace RageCoop.Server
return new(Server.MainNetServer.Connections.Where(e => e.RemoteUniqueIdentifier != local));
}
// Return a list of players within range of ...
public static List<NetConnection> GetAllInRange(LVector3 position, float range)
{
return new(Server.MainNetServer.Connections.FindAll(e =>
{
Client client = Server.Clients.First(x => x.ClientID == e.RemoteUniqueIdentifier);
return client != null && client.Player.IsInRangeOf(position, range);
}));
}
// Return a list of players within range of ... but not the local one
public static List<NetConnection> GetAllInRange(LVector3 position, float range, NetConnection local)
{
return new(Server.MainNetServer.Connections.Where(e =>
{
Client client = Server.Clients.First(x => x.ClientID == e.RemoteUniqueIdentifier);
return e != local && client != null && client.Player.IsInRangeOf(position, range);
}));
}
public static T Read<T>(string file) where T : new()
{
XmlSerializer ser = new(typeof(T));