RAGECOOP-V/Client/Main.cs
Makinolo 7b467aec18 Creates interface to communicate with other mods and menus optional
The interface static class will be used from 3rd party mods to
initiate, close and configure the connection. The project can be
compiled with the NON_INTERACTIVE copiler flag to remove the menus
and leave only the interface as a controller.
This is an initial implementation, future additions will support
more operations.
2021-09-26 21:31:40 -06:00

325 lines
12 KiB
C#

using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Drawing;
using CoopClient.Entities;
using CoopClient.Menus;
using GTA;
using GTA.Native;
namespace CoopClient
{
public class Main : Script
{
public static RelationshipGroup RelationshipGroup;
private bool GameLoaded = false;
public static readonly string CurrentModVersion = "V0_7_0";
public static bool ShareNpcsWithPlayers = false;
public static bool DisableTraffic = false;
public static bool NpcsAllowed = false;
private static bool IsGoingToCar = false;
public static Settings MainSettings = Util.ReadSettings();
public static Networking MainNetworking = new Networking();
#if !NON_INTERACTIVE
public static MenusMain MainMenu = new MenusMain();
#endif
public static Chat MainChat = new Chat();
public static long LocalClientID = 0;
public static readonly Dictionary<long, EntitiesPlayer> Players = new Dictionary<long, EntitiesPlayer>();
public static readonly Dictionary<long, EntitiesNpc> Npcs = new Dictionary<long, EntitiesNpc>();
public Main()
{
Function.Call((Hash)0x0888C3502DBBEEF5); // _LOAD_MP_DLC_MAPS
Function.Call((Hash)0x9BAE5AD2508DF078, true); // _ENABLE_MP_DLC_MAPS
Tick += OnTick;
#if !NON_INTERACTIVE
KeyDown += OnKeyDown;
#endif
Aborted += (object sender, EventArgs e) => CleanUp();
Util.NativeMemory();
}
private int LastDataSend;
private void OnTick(object sender, EventArgs e)
{
if (Game.IsLoading)
{
return;
}
else if (!GameLoaded && (GameLoaded = true))
{
RelationshipGroup = World.AddRelationshipGroup("SYNCPED");
Game.Player.Character.RelationshipGroup = RelationshipGroup;
}
#if !NON_INTERACTIVE
MainMenu.MenuPool.Process();
#endif
MainNetworking.ReceiveMessages();
if (IsGoingToCar && Game.Player.Character.IsInVehicle())
{
IsGoingToCar = false;
}
if (!MainNetworking.IsOnServer())
{
return;
}
#if DEBUG
if (MainNetworking.ShowNetworkInfo)
{
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 0), $"L: {MainNetworking.Latency * 1000:N0}ms", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 30), $"R: {MainNetworking.BytesReceived} bytes", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
new LemonUI.Elements.ScaledText(new PointF(Screen.PrimaryScreen.Bounds.Width / 2, 60), $"S: {MainNetworking.BytesSend} bytes", 0.5f) { Alignment = GTA.UI.Alignment.Center }.Draw();
}
#endif
MainChat.Tick();
// Display all players
foreach (KeyValuePair<long, EntitiesPlayer> player in Players)
{
player.Value.DisplayLocally(player.Value.Username);
}
#if DEBUG
if (UseDebug)
{
Debug();
}
#endif
if ((Environment.TickCount - LastDataSend) < (1000 / 60))
{
return;
}
MainNetworking.SendPlayerData();
LastDataSend = Environment.TickCount;
}
#if !NON_INTERACTIVE
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (MainChat.Focused)
{
MainChat.OnKeyDown(e.KeyCode);
return;
}
switch (e.KeyCode)
{
case Keys.F9:
if (MainMenu.MenuPool.AreAnyVisible)
{
MainMenu.MainMenu.Visible = false;
MainMenu.SubSettings.MainMenu.Visible = false;
}
else
{
MainMenu.MainMenu.Visible = true;
}
break;
case Keys.T:
if (MainNetworking.IsOnServer())
{
MainChat.Focused = true;
}
break;
case Keys.Y:
if (MainNetworking.IsOnServer())
{
int currentTimestamp = Environment.TickCount;
PlayerList.Pressed = (currentTimestamp - PlayerList.Pressed) < 5000 ? (currentTimestamp - 6000) : currentTimestamp;
}
break;
case Keys.G:
if (IsGoingToCar)
{
Game.Player.Character.Task.ClearAll();
IsGoingToCar = false;
}
else if (!Game.Player.Character.IsInVehicle())
{
Vehicle veh = World.GetNearbyVehicles(Game.Player.Character, 5f).FirstOrDefault();
if (veh != default)
{
for (int i = 0; i < veh.PassengerCapacity; i++)
{
if (veh.IsSeatFree((VehicleSeat)i))
{
Game.Player.Character.Task.EnterVehicle(veh, (VehicleSeat)i);
IsGoingToCar = true;
break;
}
}
}
}
break;
}
}
#endif
public static void CleanUp()
{
MainChat.Clear();
foreach (KeyValuePair<long, EntitiesPlayer> player in Players)
{
player.Value.Character?.AttachedBlip?.Delete();
player.Value.Character?.CurrentVehicle?.Delete();
player.Value.Character?.Kill();
player.Value.Character?.Delete();
player.Value.PedBlip?.Delete();
}
Players.Clear();
foreach (KeyValuePair<long, EntitiesNpc> Npc in Npcs)
{
Npc.Value.Character?.CurrentVehicle?.Delete();
Npc.Value.Character?.Kill();
Npc.Value.Character?.Delete();
}
Npcs.Clear();
foreach (Ped entity in World.GetAllPeds().Where(p => p.Handle != Game.Player.Character.Handle))
{
entity.Kill();
entity.Delete();
}
foreach (Vehicle veh in World.GetAllVehicles().Where(v => v.Handle != Game.Player.Character.CurrentVehicle?.Handle))
{
veh.Delete();
}
}
#if DEBUG
private int ArtificialLagCounter;
public static EntitiesPlayer DebugSyncPed;
public static int LastFullDebugSync = 0;
public static bool UseDebug = false;
private void Debug()
{
Ped player = Game.Player.Character;
if (!Players.ContainsKey(0))
{
Players.Add(0, new EntitiesPlayer() { SocialClubName = "DEBUG", Username = "DebugPlayer" });
DebugSyncPed = Players[0];
}
if ((Environment.TickCount - ArtificialLagCounter) < 157)
{
return;
}
bool fullSync = (Environment.TickCount - LastFullDebugSync) > 1500;
if (fullSync)
{
DebugSyncPed.ModelHash = player.Model.Hash;
DebugSyncPed.Props = Util.GetPedProps(player);
}
DebugSyncPed.Health = player.Health;
DebugSyncPed.Position = player.Position;
byte? flags;
if (!player.IsInVehicle())
{
flags = Util.GetPedFlags(player, fullSync, true);
DebugSyncPed.Rotation = player.Rotation;
DebugSyncPed.Velocity = player.Velocity;
DebugSyncPed.Speed = Util.GetPedSpeed(player);
DebugSyncPed.AimCoords = Util.GetPedAimCoords(player, false);
DebugSyncPed.CurrentWeaponHash = (int)player.Weapons.Current.Hash;
DebugSyncPed.LastSyncWasFull = (flags.Value & (byte)PedDataFlags.LastSyncWasFull) > 0;
DebugSyncPed.IsAiming = (flags.Value & (byte)PedDataFlags.IsAiming) > 0;
DebugSyncPed.IsShooting = (flags.Value & (byte)PedDataFlags.IsShooting) > 0;
DebugSyncPed.IsReloading = (flags.Value & (byte)PedDataFlags.IsReloading) > 0;
DebugSyncPed.IsJumping = (flags.Value & (byte)PedDataFlags.IsJumping) > 0;
DebugSyncPed.IsRagdoll = (flags.Value & (byte)PedDataFlags.IsRagdoll) > 0;
DebugSyncPed.IsOnFire = (flags.Value & (byte)PedDataFlags.IsOnFire) > 0;
DebugSyncPed.IsInVehicle = (flags.Value & (byte)PedDataFlags.IsInVehicle) > 0;
if (DebugSyncPed.Character != null && DebugSyncPed.Character.Exists())
{
Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, DebugSyncPed.Character.Handle, player.Handle, false);
Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, player.Handle, DebugSyncPed.Character.Handle, false);
}
}
else
{
Vehicle veh = player.CurrentVehicle;
veh.Opacity = 75;
flags = Util.GetVehicleFlags(player, veh, fullSync);
int secondaryColor;
int primaryColor;
unsafe
{
Function.Call<int>(Hash.GET_VEHICLE_COLOURS, veh, &primaryColor, &secondaryColor);
}
DebugSyncPed.VehicleModelHash = veh.Model.Hash;
DebugSyncPed.VehicleSeatIndex = (int)player.SeatIndex;
DebugSyncPed.VehiclePosition = veh.Position;
DebugSyncPed.VehicleRotation = veh.Quaternion;
DebugSyncPed.VehicleEngineHealth = veh.EngineHealth;
DebugSyncPed.VehicleVelocity = veh.Velocity;
DebugSyncPed.VehicleSpeed = veh.Speed;
DebugSyncPed.VehicleSteeringAngle = veh.SteeringAngle;
DebugSyncPed.VehicleColors = new int[] { primaryColor, secondaryColor };
DebugSyncPed.VehicleMods = Util.GetVehicleMods(veh);
DebugSyncPed.VehDoors = Util.GetVehicleDoors(veh.Doors);
DebugSyncPed.LastSyncWasFull = (flags.Value & (byte)VehicleDataFlags.LastSyncWasFull) > 0;
DebugSyncPed.IsInVehicle = (flags.Value & (byte)VehicleDataFlags.IsInVehicle) > 0;
DebugSyncPed.VehIsEngineRunning = (flags.Value & (byte)VehicleDataFlags.IsEngineRunning) > 0;
DebugSyncPed.VehAreLightsOn = (flags.Value & (byte)VehicleDataFlags.AreLightsOn) > 0;
DebugSyncPed.VehAreHighBeamsOn = (flags.Value & (byte)VehicleDataFlags.AreHighBeamsOn) > 0;
DebugSyncPed.VehIsSireneActive = (flags.Value & (byte)VehicleDataFlags.IsSirenActive) > 0;
DebugSyncPed.VehicleDead = (flags.Value & (byte)VehicleDataFlags.IsDead) > 0;
if (DebugSyncPed.MainVehicle != null && DebugSyncPed.MainVehicle.Exists() && player.IsInVehicle())
{
Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, DebugSyncPed.MainVehicle.Handle, player.CurrentVehicle.Handle, false);
Function.Call(Hash.SET_ENTITY_NO_COLLISION_ENTITY, player.CurrentVehicle.Handle, DebugSyncPed.MainVehicle.Handle, false);
}
}
int currentTimestamp = Environment.TickCount;
DebugSyncPed.LastUpdateReceived = currentTimestamp;
DebugSyncPed.Latency = currentTimestamp - ArtificialLagCounter;
ArtificialLagCounter = currentTimestamp;
if (fullSync)
{
LastFullDebugSync = currentTimestamp;
}
}
#endif
}
}