From ca364bb51a8cef99ec4f17b94ff191ce546b261d Mon Sep 17 00:00:00 2001 From: k0 <21180271+k073l@users.noreply.github.com> Date: Mon, 19 May 2025 21:05:47 +0200 Subject: [PATCH 1/5] feat: simple vehicle spawning --- S1API/Vehicles/LandVehicle.cs | 177 +++++++++++++++++++++++++++++++++ S1API/Vehicles/VehicleColor.cs | 26 +++++ 2 files changed, 203 insertions(+) create mode 100644 S1API/Vehicles/LandVehicle.cs create mode 100644 S1API/Vehicles/VehicleColor.cs diff --git a/S1API/Vehicles/LandVehicle.cs b/S1API/Vehicles/LandVehicle.cs new file mode 100644 index 00000000..941d4a3f --- /dev/null +++ b/S1API/Vehicles/LandVehicle.cs @@ -0,0 +1,177 @@ +#if (IL2CPPMELON) +using S1Vehicles = Il2CppScheduleOne.Vehicles; +using Il2CppScheduleOne.DevUtilities; +using Il2Cpp; +using Il2CppFishNet; +using Il2CppFishNet.Connection; +#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) +using S1Vehicles = ScheduleOne.Vehicles; +using ScheduleOne.DevUtilities; +using FishNet; +using FishNet.Connection; +#endif +using System.Reflection; +using UnityEngine; +using S1API.Logging; + +namespace S1API.Vehicles +{ + /// + /// Represents a land vehicle in the game. + /// + public class LandVehicle + { + /// + /// Logger for the LandVehicle class. + /// + private static readonly Log Logger = new Log("S1API.LandVehicle"); + + /// + /// INTERNAL: The stored reference to the land vehicle in-game (see ). + /// + internal S1Vehicles.LandVehicle S1LandVehicle = null!; + + /// + /// The stored reference to protected vehiclePrice field in the land vehicle in-game. + /// + private static readonly FieldInfo? VehiclePriceField = + typeof(S1Vehicles.LandVehicle).GetField("vehiclePrice", BindingFlags.NonPublic); + + /// + /// Connection to the player that owns the vehicle. + /// + private NetworkConnection? _conn; + + /// + /// Creates a new LandVehicle instance. + /// + public LandVehicle(string vehicleCode) + { + var vehiclePrefab = NetworkSingleton.Instance.GetVehiclePrefab(vehicleCode); + if (vehiclePrefab == null) + { + Logger.Error($"SpawnVehicle: '{vehicleCode}' is not a valid vehicle code!"); + return; + } + + var component = UnityEngine.Object.Instantiate(vehiclePrefab.gameObject) + .GetComponent(); + + component.SetGUID(GUIDManager.GenerateUniqueGUID()); + NetworkSingleton.Instance.AllVehicles.Add(component); + + S1LandVehicle = component; + _setConnection(); + } + + /// + /// INTERNAL: Creates a LandVehicle instance from an in-game land vehicle instance. + /// + /// The in-game land vehicle instance. + internal LandVehicle(S1Vehicles.LandVehicle landVehicle) + { + S1LandVehicle = landVehicle; + _setConnection(); + } + + /// + /// Sets the connection to the player that owns the vehicle. + /// + private void _setConnection() + { + var nm = InstanceFinder.NetworkManager; + if (nm.IsClientOnly) + { + var tempConn = InstanceFinder.ClientManager.Connection; + if (tempConn != null && tempConn.IsValid) + _conn = tempConn; + } + else if (nm.IsServerOnly || (nm.IsServer && !nm.IsClient)) + { + var owner = S1LandVehicle.Owner; + if (owner != null && owner.IsValid) + _conn = owner; + } + } + + /// + /// Vehicle price. + /// + public float VehiclePrice + { + get => S1LandVehicle.VehiclePrice; + set => VehiclePriceField?.SetValue(S1LandVehicle, value); + } + + /// + /// Vehicle's top speed. + /// + public float TopSpeed + { + get => S1LandVehicle.TopSpeed; + set => S1LandVehicle.TopSpeed = value; + } + + /// + /// If the vehicle is owned by the player. + /// + public bool IsPlayerOwned + { + get => S1LandVehicle.IsPlayerOwned; + set => _setIsPlayerOwned(value); + } + + /// + /// Helper method to set the vehicle as player owned. + /// + /// If true, sets vehicle as player owned + private void _setIsPlayerOwned(bool isPlayerOwned) + { + S1LandVehicle.SetIsPlayerOwned(_conn, isPlayerOwned); + // make sure to add/remove the vehicle from the player owned vehicles list + if (isPlayerOwned) + NetworkSingleton.Instance.PlayerOwnedVehicles.Add(S1LandVehicle); + else + NetworkSingleton.Instance.PlayerOwnedVehicles.Remove(S1LandVehicle); + } + + /// + /// Vehicle's color. + /// + public VehicleColor Color + { + get => (VehicleColor)S1LandVehicle.OwnedColor; + set => _setColor(value); + } + + /// + /// Helper method to set the vehicle color. + /// + /// Vehicle's color + private void _setColor(VehicleColor color) + { + var setOwnedColorMethod = + typeof(S1Vehicles.LandVehicle).GetMethod("SetOwnedColor", + BindingFlags.Instance | BindingFlags.NonPublic); + if (setOwnedColorMethod == null) + { + Logger.Error("SetOwnedColor method not found!"); + return; + } + + setOwnedColorMethod.Invoke(S1LandVehicle, [_conn, (S1Vehicles.Modification.EVehicleColor)color]); + } + + /// + /// Spawns the vehicle in the game world. + /// + /// Position in the world + /// Rotation of the vehicle + public void Spawn(Vector3 position, Quaternion rotation) + { + S1LandVehicle.transform.position = position; + S1LandVehicle.transform.rotation = rotation; + NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); + } + } +} \ No newline at end of file diff --git a/S1API/Vehicles/VehicleColor.cs b/S1API/Vehicles/VehicleColor.cs new file mode 100644 index 00000000..4694d3a5 --- /dev/null +++ b/S1API/Vehicles/VehicleColor.cs @@ -0,0 +1,26 @@ +namespace S1API.Vehicles +{ + /// + /// Represents available colors for vehicles. + /// + public enum VehicleColor + { + Black, + DarkGrey, + LightGrey, + White, + Yellow, + Orange, + Red, + DullRed, + Pink, + Purple, + Navy, + DarkBlue, + LightBlue, + Cyan, + LightGreen, + DarkGreen, + Custom + } +} \ No newline at end of file From 000b93dc302ac00ab955e74d4e01df6fec64c627 Mon Sep 17 00:00:00 2001 From: k0 <21180271+k073l@users.noreply.github.com> Date: Mon, 19 May 2025 21:15:53 +0200 Subject: [PATCH 2/5] fix: don't spawn if not host --- S1API/Vehicles/LandVehicle.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/S1API/Vehicles/LandVehicle.cs b/S1API/Vehicles/LandVehicle.cs index 941d4a3f..f46a2e12 100644 --- a/S1API/Vehicles/LandVehicle.cs +++ b/S1API/Vehicles/LandVehicle.cs @@ -169,6 +169,12 @@ private void _setColor(VehicleColor color) /// Rotation of the vehicle public void Spawn(Vector3 position, Quaternion rotation) { + if (!InstanceFinder.IsServer) + { + Logger.Warning("Spawn can only be called on the server!"); + return; + } + S1LandVehicle.transform.position = position; S1LandVehicle.transform.rotation = rotation; NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); From d804cb3d51576b8f50811fceadc3dfff0f22d4f2 Mon Sep 17 00:00:00 2001 From: k0 <21180271+k073l@users.noreply.github.com> Date: Tue, 20 May 2025 14:44:49 +0200 Subject: [PATCH 3/5] reorder fields and methods, rename private members --- S1API/Vehicles/LandVehicle.cs | 175 ++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 81 deletions(-) diff --git a/S1API/Vehicles/LandVehicle.cs b/S1API/Vehicles/LandVehicle.cs index f46a2e12..e8447ba3 100644 --- a/S1API/Vehicles/LandVehicle.cs +++ b/S1API/Vehicles/LandVehicle.cs @@ -21,48 +21,93 @@ namespace S1API.Vehicles /// public class LandVehicle { + // Public members intended to be used by modders + #region Public Members /// - /// Logger for the LandVehicle class. + /// Creates a new LandVehicle instance. /// - private static readonly Log Logger = new Log("S1API.LandVehicle"); + public LandVehicle(string vehicleCode) + { + var vehiclePrefab = NetworkSingleton.Instance.GetVehiclePrefab(vehicleCode); + if (vehiclePrefab == null) + { + _logger.Error($"SpawnVehicle: '{vehicleCode}' is not a valid vehicle code!"); + return; + } + + var component = UnityEngine.Object.Instantiate(vehiclePrefab.gameObject) + .GetComponent(); + + component.SetGUID(GUIDManager.GenerateUniqueGUID()); + NetworkSingleton.Instance.AllVehicles.Add(component); + + S1LandVehicle = component; + SetConnection(); + } /// - /// INTERNAL: The stored reference to the land vehicle in-game (see ). + /// Vehicle price. /// - internal S1Vehicles.LandVehicle S1LandVehicle = null!; + public float VehiclePrice + { + get => S1LandVehicle.VehiclePrice; + set => VehiclePriceField?.SetValue(S1LandVehicle, value); + } /// - /// The stored reference to protected vehiclePrice field in the land vehicle in-game. + /// Vehicle's top speed. /// - private static readonly FieldInfo? VehiclePriceField = - typeof(S1Vehicles.LandVehicle).GetField("vehiclePrice", BindingFlags.NonPublic); + public float TopSpeed + { + get => S1LandVehicle.TopSpeed; + set => S1LandVehicle.TopSpeed = value; + } /// - /// Connection to the player that owns the vehicle. + /// If the vehicle is owned by the player. /// - private NetworkConnection? _conn; + public bool IsPlayerOwned + { + get => S1LandVehicle.IsPlayerOwned; + set => SetIsPlayerOwned(value); + } /// - /// Creates a new LandVehicle instance. + /// Vehicle's color. /// - public LandVehicle(string vehicleCode) + public VehicleColor Color { - var vehiclePrefab = NetworkSingleton.Instance.GetVehiclePrefab(vehicleCode); - if (vehiclePrefab == null) + get => (VehicleColor)S1LandVehicle.OwnedColor; + set => SetColor(value); + } + + /// + /// Spawns the vehicle in the game world. + /// + /// Position in the world + /// Rotation of the vehicle + public void Spawn(Vector3 position, Quaternion rotation) + { + if (!InstanceFinder.IsServer) { - Logger.Error($"SpawnVehicle: '{vehicleCode}' is not a valid vehicle code!"); + _logger.Warning("Spawn can only be called on the server!"); return; } - var component = UnityEngine.Object.Instantiate(vehiclePrefab.gameObject) - .GetComponent(); - - component.SetGUID(GUIDManager.GenerateUniqueGUID()); - NetworkSingleton.Instance.AllVehicles.Add(component); - - S1LandVehicle = component; - _setConnection(); + S1LandVehicle.transform.position = position; + S1LandVehicle.transform.rotation = rotation; + NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); } + + #endregion + + // Internal members used by S1API + #region Internal Members + + /// + /// INTERNAL: The stored reference to the land vehicle in-game (see ). + /// + internal S1Vehicles.LandVehicle S1LandVehicle = null!; /// /// INTERNAL: Creates a LandVehicle instance from an in-game land vehicle instance. @@ -71,13 +116,34 @@ public LandVehicle(string vehicleCode) internal LandVehicle(S1Vehicles.LandVehicle landVehicle) { S1LandVehicle = landVehicle; - _setConnection(); + SetConnection(); } + + #endregion + + // Private members used by LandVehicle class + #region Private Members + + /// + /// Logger for the LandVehicle class. + /// + private static readonly Log _logger = new Log("S1API.LandVehicle"); + + /// + /// The stored reference to protected vehiclePrice field in the land vehicle in-game. + /// + private static readonly FieldInfo? VehiclePriceField = + typeof(S1Vehicles.LandVehicle).GetField("vehiclePrice", BindingFlags.NonPublic); + + /// + /// Connection to the player that owns the vehicle. + /// + private NetworkConnection? _conn; /// /// Sets the connection to the player that owns the vehicle. /// - private void _setConnection() + private void SetConnection() { var nm = InstanceFinder.NetworkManager; if (nm.IsClientOnly) @@ -94,38 +160,11 @@ private void _setConnection() } } - /// - /// Vehicle price. - /// - public float VehiclePrice - { - get => S1LandVehicle.VehiclePrice; - set => VehiclePriceField?.SetValue(S1LandVehicle, value); - } - - /// - /// Vehicle's top speed. - /// - public float TopSpeed - { - get => S1LandVehicle.TopSpeed; - set => S1LandVehicle.TopSpeed = value; - } - - /// - /// If the vehicle is owned by the player. - /// - public bool IsPlayerOwned - { - get => S1LandVehicle.IsPlayerOwned; - set => _setIsPlayerOwned(value); - } - /// /// Helper method to set the vehicle as player owned. /// /// If true, sets vehicle as player owned - private void _setIsPlayerOwned(bool isPlayerOwned) + private void SetIsPlayerOwned(bool isPlayerOwned) { S1LandVehicle.SetIsPlayerOwned(_conn, isPlayerOwned); // make sure to add/remove the vehicle from the player owned vehicles list @@ -135,49 +174,23 @@ private void _setIsPlayerOwned(bool isPlayerOwned) NetworkSingleton.Instance.PlayerOwnedVehicles.Remove(S1LandVehicle); } - /// - /// Vehicle's color. - /// - public VehicleColor Color - { - get => (VehicleColor)S1LandVehicle.OwnedColor; - set => _setColor(value); - } - /// /// Helper method to set the vehicle color. /// /// Vehicle's color - private void _setColor(VehicleColor color) + private void SetColor(VehicleColor color) { var setOwnedColorMethod = typeof(S1Vehicles.LandVehicle).GetMethod("SetOwnedColor", BindingFlags.Instance | BindingFlags.NonPublic); if (setOwnedColorMethod == null) { - Logger.Error("SetOwnedColor method not found!"); + _logger.Error("SetOwnedColor method not found!"); return; } setOwnedColorMethod.Invoke(S1LandVehicle, [_conn, (S1Vehicles.Modification.EVehicleColor)color]); } - - /// - /// Spawns the vehicle in the game world. - /// - /// Position in the world - /// Rotation of the vehicle - public void Spawn(Vector3 position, Quaternion rotation) - { - if (!InstanceFinder.IsServer) - { - Logger.Warning("Spawn can only be called on the server!"); - return; - } - - S1LandVehicle.transform.position = position; - S1LandVehicle.transform.rotation = rotation; - NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); - } + #endregion } } \ No newline at end of file From a84dffbee797cbac54e067d94c050825284113b5 Mon Sep 17 00:00:00 2001 From: k0 <21180271+k073l@users.noreply.github.com> Date: Tue, 20 May 2025 14:48:44 +0200 Subject: [PATCH 4/5] throw exception if user tries to spawn null vehicle --- S1API/Vehicles/LandVehicle.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/S1API/Vehicles/LandVehicle.cs b/S1API/Vehicles/LandVehicle.cs index e8447ba3..55475536 100644 --- a/S1API/Vehicles/LandVehicle.cs +++ b/S1API/Vehicles/LandVehicle.cs @@ -10,6 +10,7 @@ using FishNet; using FishNet.Connection; #endif +using System; using System.Reflection; using UnityEngine; using S1API.Logging; @@ -94,6 +95,9 @@ public void Spawn(Vector3 position, Quaternion rotation) return; } + if (S1LandVehicle == null) + throw new Exception("Unable to spawn vehicle, S1LandVehicle is null!"); + S1LandVehicle.transform.position = position; S1LandVehicle.transform.rotation = rotation; NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); From 1c91ed9776f3726b1d284cb0b205db028174a350 Mon Sep 17 00:00:00 2001 From: k0 <21180271+k073l@users.noreply.github.com> Date: Tue, 20 May 2025 18:57:33 +0200 Subject: [PATCH 5/5] removed NetworkSingleton generics --- S1API/Vehicles/LandVehicle.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/S1API/Vehicles/LandVehicle.cs b/S1API/Vehicles/LandVehicle.cs index 55475536..3f91bdd7 100644 --- a/S1API/Vehicles/LandVehicle.cs +++ b/S1API/Vehicles/LandVehicle.cs @@ -1,12 +1,10 @@ #if (IL2CPPMELON) using S1Vehicles = Il2CppScheduleOne.Vehicles; -using Il2CppScheduleOne.DevUtilities; using Il2Cpp; using Il2CppFishNet; using Il2CppFishNet.Connection; #elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX) using S1Vehicles = ScheduleOne.Vehicles; -using ScheduleOne.DevUtilities; using FishNet; using FishNet.Connection; #endif @@ -29,7 +27,7 @@ public class LandVehicle /// public LandVehicle(string vehicleCode) { - var vehiclePrefab = NetworkSingleton.Instance.GetVehiclePrefab(vehicleCode); + var vehiclePrefab = S1Vehicles.VehicleManager.Instance.GetVehiclePrefab(vehicleCode); if (vehiclePrefab == null) { _logger.Error($"SpawnVehicle: '{vehicleCode}' is not a valid vehicle code!"); @@ -40,7 +38,7 @@ public LandVehicle(string vehicleCode) .GetComponent(); component.SetGUID(GUIDManager.GenerateUniqueGUID()); - NetworkSingleton.Instance.AllVehicles.Add(component); + S1Vehicles.VehicleManager.Instance.AllVehicles.Add(component); S1LandVehicle = component; SetConnection(); @@ -100,7 +98,7 @@ public void Spawn(Vector3 position, Quaternion rotation) S1LandVehicle.transform.position = position; S1LandVehicle.transform.rotation = rotation; - NetworkSingleton.Instance.Spawn(S1LandVehicle.gameObject); + S1Vehicles.VehicleManager.Instance.Spawn(S1LandVehicle.gameObject); } #endregion @@ -173,9 +171,9 @@ private void SetIsPlayerOwned(bool isPlayerOwned) S1LandVehicle.SetIsPlayerOwned(_conn, isPlayerOwned); // make sure to add/remove the vehicle from the player owned vehicles list if (isPlayerOwned) - NetworkSingleton.Instance.PlayerOwnedVehicles.Add(S1LandVehicle); + S1Vehicles.VehicleManager.Instance.PlayerOwnedVehicles.Add(S1LandVehicle); else - NetworkSingleton.Instance.PlayerOwnedVehicles.Remove(S1LandVehicle); + S1Vehicles.VehicleManager.Instance.PlayerOwnedVehicles.Remove(S1LandVehicle); } ///