2020-05-31 17:07:22 +00:00
using UnityEngine ;
2019-04-29 18:27:17 +00:00
using SanAndreasUnity.Net ;
2019-04-29 22:18:19 +00:00
using Mirror ;
2019-04-29 22:40:26 +00:00
using SanAndreasUnity.Utilities ;
2019-04-30 22:33:01 +00:00
using System.Linq ;
2020-05-31 17:07:22 +00:00
namespace SanAndreasUnity.Behaviours.Vehicles
{
2019-04-29 22:18:19 +00:00
public class VehicleController : NetworkBehaviour
2020-05-31 17:07:22 +00:00
{
2019-04-29 22:11:14 +00:00
private Vehicle m_vehicle ;
2019-05-22 23:32:54 +00:00
bool IsControlledByLocalPlayer = > m_vehicle . IsControlledByLocalPlayer ;
2019-04-29 22:18:19 +00:00
2019-04-29 23:23:31 +00:00
[SyncVar] int m_net_id ;
2019-04-30 22:33:01 +00:00
[SyncVar] string m_net_carColors ;
2019-04-29 23:23:31 +00:00
[SyncVar] float m_net_acceleration ;
[SyncVar] float m_net_steering ;
[SyncVar] float m_net_braking ;
2019-05-23 20:48:18 +00:00
[SyncVar(hook=nameof(OnNetPositionChanged))] Vector3 m_net_position ;
[SyncVar(hook=nameof(OnNetRotationChanged))] Quaternion m_net_rotation ;
2019-04-30 00:37:29 +00:00
[SyncVar] Vector3 m_net_linearVelocity ;
[SyncVar] Vector3 m_net_angularVelocity ;
2020-07-01 17:31:06 +00:00
[SyncVar] float m_net_health ;
2019-05-22 19:18:12 +00:00
struct WheelSyncData
{
public float brakeTorque ;
public float motorTorque ;
public float steerAngle ;
//public float travel;
2021-02-04 21:21:16 +00:00
public float localPosY ;
2021-02-04 22:18:01 +00:00
public float rpm ;
2019-05-22 19:18:12 +00:00
}
class WheelSyncList : SyncList < WheelSyncData > { }
WheelSyncList m_net_wheelsData ;
2020-05-31 17:07:22 +00:00
2019-04-29 23:23:31 +00:00
// is it better to place syncvars in Vehicle class ? - that way, there is no need for hooks
// - or we could assign/read syncvars in Update()
2020-05-31 17:07:22 +00:00
2019-04-29 22:18:19 +00:00
2020-05-31 17:07:22 +00:00
private void Awake ( )
{
2019-04-29 22:40:26 +00:00
//m_vehicle = GetComponent<Vehicle>();
2020-05-31 17:07:22 +00:00
}
2019-04-29 23:41:18 +00:00
internal void OnAfterCreateVehicle ( )
{
m_vehicle = this . GetComponent < Vehicle > ( ) ;
m_net_id = m_vehicle . Definition . Id ;
2020-07-05 19:39:59 +00:00
m_net_carColors = SerializeColors ( m_vehicle . Colors ) ;
2019-04-29 23:41:18 +00:00
}
2019-04-29 22:18:19 +00:00
public override void OnStartClient ( )
{
base . OnStartClient ( ) ;
if ( ! NetStatus . IsServer )
{
2019-04-29 22:40:26 +00:00
F . RunExceptionSafe ( ( ) = > {
2019-05-05 22:26:55 +00:00
// load vehicle on clients
2020-07-05 19:39:59 +00:00
int [ ] colors = DeserializeColors ( m_net_carColors ) ;
2019-04-30 22:33:01 +00:00
m_vehicle = Vehicle . Create ( this . gameObject , m_net_id , colors , this . transform . position , this . transform . rotation ) ;
2019-05-05 22:26:55 +00:00
// update rigid body status
this . EnableOrDisableRigidBody ( ) ;
2021-02-05 00:04:37 +00:00
if ( VehicleManager . Instance . destroyWheelCollidersOnClient )
{
foreach ( var wheelCollider in this . GetComponentsInChildren < WheelCollider > ( ) )
{
Destroy ( wheelCollider ) ;
}
}
2019-04-29 22:40:26 +00:00
} ) ;
2019-04-29 22:18:19 +00:00
}
}
2019-05-05 22:26:55 +00:00
public override void OnStartAuthority ( )
{
base . OnStartAuthority ( ) ;
this . EnableOrDisableRigidBody ( ) ;
}
public override void OnStopAuthority ( )
{
base . OnStopAuthority ( ) ;
this . EnableOrDisableRigidBody ( ) ;
}
2020-07-05 19:39:59 +00:00
public static string SerializeColors ( int [ ] colors )
{
2020-07-05 20:35:21 +00:00
return colors ! = null ? string . Join ( ";" , colors . Select ( c = > c . ToString ( System . Globalization . CultureInfo . InvariantCulture ) ) ) : null ;
2020-07-05 19:39:59 +00:00
}
public static int [ ] DeserializeColors ( string colors )
{
2020-07-05 20:35:21 +00:00
return string . IsNullOrEmpty ( colors ) ? null : colors . Split ( ';' ) . Select ( s = > int . Parse ( s , System . Globalization . CultureInfo . InvariantCulture ) ) . ToArray ( ) ;
2020-07-05 19:39:59 +00:00
}
2020-05-31 17:07:22 +00:00
private void Update ( )
{
2019-04-29 18:27:17 +00:00
2019-05-24 00:08:36 +00:00
// if syncvars are used for updating transform, then disable NetworkTransform, and vice versa
m_vehicle . NetTransform . enabled = ! VehicleManager . Instance . syncVehicleTransformUsingSyncVars ;
2019-05-28 15:09:44 +00:00
// update status of rigid body
this . EnableOrDisableRigidBody ( ) ;
2019-04-29 23:41:18 +00:00
this . ProcessSyncvars ( ) ;
2019-04-29 22:11:14 +00:00
var driverSeat = m_vehicle . DriverSeat ;
2019-04-29 18:27:17 +00:00
if ( null = = driverSeat | | null = = driverSeat . OccupyingPed )
{
if ( NetStatus . IsServer )
this . ResetInput ( ) ;
return ;
}
2019-05-22 16:59:16 +00:00
if ( null = = Ped . Instance | | driverSeat . OccupyingPed ! = Ped . Instance )
2019-04-29 18:27:17 +00:00
return ;
// local ped is occupying driver seat
2019-05-24 21:26:31 +00:00
float oldAcc = m_vehicle . Accelerator ;
float oldBrake = m_vehicle . Braking ;
float oldSteer = m_vehicle . Steering ;
2019-04-29 22:59:21 +00:00
if ( ! GameManager . CanPlayerReadInput ( ) )
this . ResetInput ( ) ;
else
this . ReadInput ( ) ;
2019-04-29 18:27:17 +00:00
2019-05-05 23:07:35 +00:00
// why do we send input ?
// - so that everyone knows if the gas/brake is pressed, and can simulate wheel effects
// - so that server can predict position and velocity of rigid body
2019-04-29 22:59:21 +00:00
PedSync . Local . SendVehicleInput ( m_vehicle . Accelerator , m_vehicle . Steering , m_vehicle . Braking ) ;
2019-05-05 23:07:35 +00:00
// TODO: also send velocity of rigid body
2019-05-24 21:26:31 +00:00
if ( ! NetStatus . IsServer & & ! VehicleManager . Instance . controlInputOnLocalPlayer )
{
// local player should not control input, so restore old input
m_vehicle . Accelerator = oldAcc ;
m_vehicle . Braking = oldBrake ;
m_vehicle . Steering = oldSteer ;
}
2019-04-29 22:59:21 +00:00
}
2019-04-29 23:41:18 +00:00
void ProcessSyncvars ( )
{
if ( NetStatus . IsServer )
{
m_net_acceleration = m_vehicle . Accelerator ;
m_net_steering = m_vehicle . Steering ;
m_net_braking = m_vehicle . Braking ;
2021-02-05 00:06:11 +00:00
m_net_position = m_vehicle . RigidBody . position ;
m_net_rotation = m_vehicle . RigidBody . rotation ;
2019-04-30 00:37:29 +00:00
m_net_linearVelocity = m_vehicle . RigidBody . velocity ;
m_net_angularVelocity = m_vehicle . RigidBody . angularVelocity ;
2020-07-01 17:31:06 +00:00
m_net_health = m_vehicle . Health ;
2019-05-22 19:18:12 +00:00
// wheels
2019-07-15 22:15:11 +00:00
m_net_wheelsData . Flush ( ) ; // remove current list of changes - this ensures that only the current wheel state is sent, and prevents memory leak bug in Mirror
2019-05-22 19:18:12 +00:00
m_net_wheelsData . Clear ( ) ;
foreach ( var wheel in m_vehicle . Wheels ) {
m_net_wheelsData . Add ( new WheelSyncData ( ) {
brakeTorque = wheel . Collider . brakeTorque ,
motorTorque = wheel . Collider . motorTorque ,
steerAngle = wheel . Collider . steerAngle ,
//travel = wheel.Travel,
2021-02-05 00:04:37 +00:00
localPosY = wheel . Child . localPosition . y ,
2021-02-04 22:18:01 +00:00
rpm = wheel . Collider . rpm ,
2019-05-22 19:18:12 +00:00
} ) ;
}
2019-04-29 23:41:18 +00:00
}
2019-05-22 17:37:17 +00:00
else
2019-04-29 23:41:18 +00:00
{
2019-05-24 21:26:31 +00:00
// apply input
if ( ! this . IsControlledByLocalPlayer | | ( this . IsControlledByLocalPlayer & & ! VehicleManager . Instance . controlInputOnLocalPlayer ) )
2019-05-22 17:37:17 +00:00
{
m_vehicle . Accelerator = m_net_acceleration ;
m_vehicle . Steering = m_net_steering ;
m_vehicle . Braking = m_net_braking ;
2019-05-24 20:48:00 +00:00
}
2019-05-22 19:18:12 +00:00
2019-05-24 20:48:00 +00:00
// update wheels
if ( ! this . IsControlledByLocalPlayer | | ( this . IsControlledByLocalPlayer & & ! VehicleManager . Instance . controlWheelsOnLocalPlayer ) )
{
2019-05-22 19:18:12 +00:00
for ( int i = 0 ; i < m_vehicle . Wheels . Count & & i < m_net_wheelsData . Count ; i + + ) {
var w = m_vehicle . Wheels [ i ] ;
var data = m_net_wheelsData [ i ] ;
2021-02-05 00:04:37 +00:00
if ( w . Collider ! = null )
{
w . Collider . brakeTorque = data . brakeTorque ;
w . Collider . motorTorque = data . motorTorque ;
w . Collider . steerAngle = data . steerAngle ;
}
2019-05-22 19:18:12 +00:00
//w.Travel = data.travel;
2021-02-05 00:04:37 +00:00
w . Child . SetLocalY ( data . localPosY ) ;
2021-02-04 22:18:01 +00:00
Vehicle . UpdateWheelRotation ( w , data . rpm , data . steerAngle ) ;
2019-05-22 19:18:12 +00:00
}
2019-05-22 17:37:17 +00:00
}
2019-05-23 20:48:18 +00:00
// position and rotation will be applied in syncvar hooks
2019-05-22 17:37:17 +00:00
// apply velocity on all clients
2021-02-04 21:14:10 +00:00
2019-04-30 00:37:29 +00:00
if ( VehicleManager . Instance . syncLinearVelocity )
m_vehicle . RigidBody . velocity = m_net_linearVelocity ;
2021-02-04 21:14:10 +00:00
else
m_vehicle . RigidBody . velocity = Vector3 . zero ;
2019-04-30 00:37:29 +00:00
if ( VehicleManager . Instance . syncAngularVelocity )
m_vehicle . RigidBody . angularVelocity = m_net_angularVelocity ;
2021-02-04 21:14:10 +00:00
else
m_vehicle . RigidBody . angularVelocity = Vector3 . zero ;
2020-07-01 17:31:06 +00:00
m_vehicle . Health = m_net_health ;
2019-04-29 23:41:18 +00:00
}
}
2019-04-29 22:59:21 +00:00
void ResetInput ( )
{
m_vehicle . Accelerator = 0 ;
m_vehicle . Steering = 0 ;
m_vehicle . Braking = 0 ;
}
2020-05-31 17:07:22 +00:00
2019-04-29 22:59:21 +00:00
void ReadInput ( )
{
2019-07-25 01:50:57 +00:00
var customInput = CustomInput . Instance ;
var accel = customInput . GetAxis ( "Vertical" ) ;
var brake = customInput . GetButton ( "Brake" ) ? 1.0f : 0.0f ;
2019-04-29 22:11:14 +00:00
var speed = Vector3 . Dot ( m_vehicle . Velocity , m_vehicle . transform . forward ) ;
2020-05-31 17:07:22 +00:00
if ( speed * accel < 0f )
{
brake = Mathf . Max ( brake , 0.75f ) ;
accel = 0f ;
}
2019-04-29 22:11:14 +00:00
m_vehicle . Accelerator = accel ;
2019-07-25 01:50:57 +00:00
m_vehicle . Steering = customInput . GetAxis ( "Horizontal" ) ;
2019-04-29 22:11:14 +00:00
m_vehicle . Braking = brake ;
2020-05-31 17:07:22 +00:00
}
2019-04-29 18:27:17 +00:00
2019-05-26 15:04:47 +00:00
public void EnableOrDisableRigidBody ( )
2019-05-05 22:26:55 +00:00
{
2019-05-28 14:31:42 +00:00
if ( NetStatus . IsServer )
2019-05-05 22:26:55 +00:00
{
2019-05-26 15:04:47 +00:00
F . EnableRigidBody ( m_vehicle . RigidBody ) ;
2019-05-28 14:31:42 +00:00
return ;
2019-05-05 22:26:55 +00:00
}
2019-05-28 14:31:42 +00:00
if ( VehicleManager . Instance . whenToDisableRigidBody . Matches ( this . IsControlledByLocalPlayer , ! NetStatus . IsServer ) )
F . DisableRigidBody ( m_vehicle . RigidBody ) ;
2019-05-05 22:26:55 +00:00
else
2019-05-28 14:31:42 +00:00
F . EnableRigidBody ( m_vehicle . RigidBody ) ;
2019-05-05 22:26:55 +00:00
}
2019-05-23 20:48:18 +00:00
void OnNetPositionChanged ( Vector3 pos )
{
if ( NetStatus . IsServer )
return ;
2019-05-23 23:32:37 +00:00
if ( VehicleManager . Instance . syncVehicleTransformUsingSyncVars ) {
if ( m_vehicle ! = null & & m_vehicle . RigidBody ! = null )
m_vehicle . RigidBody . MovePosition ( pos ) ;
}
2019-05-23 20:48:18 +00:00
}
void OnNetRotationChanged ( Quaternion rot )
{
if ( NetStatus . IsServer )
return ;
2019-05-23 23:32:37 +00:00
if ( VehicleManager . Instance . syncVehicleTransformUsingSyncVars ) {
if ( m_vehicle ! = null & & m_vehicle . RigidBody ! = null )
m_vehicle . RigidBody . MoveRotation ( rot ) ;
}
2019-05-23 20:48:18 +00:00
}
2020-05-31 17:07:22 +00:00
}
}