diff --git a/Multiplayer/Components/MainMenu/ServerBrowser/ServerBrowserElement.cs b/Multiplayer/Components/MainMenu/ServerBrowser/ServerBrowserElement.cs
index 18476b38..5110137e 100644
--- a/Multiplayer/Components/MainMenu/ServerBrowser/ServerBrowserElement.cs
+++ b/Multiplayer/Components/MainMenu/ServerBrowser/ServerBrowserElement.cs
@@ -76,7 +76,7 @@ protected override void Awake()
goIconLAN = this.FindChildByName("LAN Icon");
}
else
- {
+ {
goIconLAN = Instantiate(goIconPassword, goIconPassword.transform.parent);
goIconLAN.name = "LAN Icon";
Vector3 LANpos = goIconLAN.transform.localPosition;
@@ -113,7 +113,7 @@ public void UpdateView()
serverName.text = data.Name;
playerCount.text = $"{data.CurrentPlayers} / {data.MaxPlayers}";
- ping.text = $"{(data.Ping < 0 ? "?" : data.Ping)} ms";
+ ping.text = $"{(data.Ping < 0 ? "?" : data.Ping.ToString())} ms";
// Hide the icon if the server does not have a password
goIconPassword.SetActive(data.HasPassword);
diff --git a/Multiplayer/Networking/Managers/Client/NetworkClient.cs b/Multiplayer/Networking/Managers/Client/NetworkClient.cs
index f2f22879..4eceeffc 100644
--- a/Multiplayer/Networking/Managers/Client/NetworkClient.cs
+++ b/Multiplayer/Networking/Managers/Client/NetworkClient.cs
@@ -1293,6 +1293,11 @@ public void SendTrainSyncRequest(ushort netId)
}, DeliveryMethod.ReliableUnordered);
}
+ public void SendTrainCarSpawnRequest(TrainCar trainCar)
+ {
+ SendPacketToServer(ServerboundTrainCarSpawnRequest.FromTrainCar(trainCar), DeliveryMethod.ReliableOrdered);
+ }
+
public void SendTrainDeleteRequest(ushort netId)
{
SendPacketToServer(new ServerboundTrainDeleteRequestPacket
diff --git a/Multiplayer/Networking/Managers/NetworkManager.cs b/Multiplayer/Networking/Managers/NetworkManager.cs
index 68d06194..3bf9e5ab 100644
--- a/Multiplayer/Networking/Managers/NetworkManager.cs
+++ b/Multiplayer/Networking/Managers/NetworkManager.cs
@@ -29,7 +29,7 @@ public abstract class NetworkManager
protected NetworkManager(Settings settings)
{
netPacketProcessor = new NetPacketProcessor();
- //transport = new LiteNetLibTransport();
+ // transport = new LiteNetLibTransport();
transport = new SteamWorksTransport();
transport.OnConnectionRequest += OnConnectionRequest;
diff --git a/Multiplayer/Networking/Managers/Server/NetworkServer.cs b/Multiplayer/Networking/Managers/Server/NetworkServer.cs
index a71b1def..07e322fe 100644
--- a/Multiplayer/Networking/Managers/Server/NetworkServer.cs
+++ b/Multiplayer/Networking/Managers/Server/NetworkServer.cs
@@ -31,6 +31,7 @@
using Multiplayer.Networking.Packets.Serverbound.Train;
using Multiplayer.Networking.Packets.Unconnected;
using System.Text;
+using Multiplayer.Components;
using Multiplayer.Networking.Data.Train;
using Multiplayer.Networking.TransportLayers;
using Multiplayer.Networking.Packets.Serverbound.Jobs;
@@ -131,6 +132,7 @@ protected override void Subscribe()
netPacketProcessor.SubscribeReusable(OnServerboundPlayerPositionPacket);
netPacketProcessor.SubscribeReusable(OnServerboundTrainSyncRequestPacket);
+ netPacketProcessor.SubscribeReusable(OnServerboundTrainCarSpawnRequest);
netPacketProcessor.SubscribeReusable(OnServerboundTrainDeleteRequestPacket);
netPacketProcessor.SubscribeReusable(OnServerboundTrainRerailRequestPacket);
netPacketProcessor.SubscribeReusable(OnServerboundLicensePurchaseRequestPacket);
@@ -208,7 +210,7 @@ public override void OnPeerConnected(ITransportPeer peer)
public override void OnPeerDisconnected(ITransportPeer peer, DisconnectReason disconnectReason)
{
byte id = (byte)peer.Id;
- Log($"Player {(serverPlayers.TryGetValue(id, out ServerPlayer player) ? player : id)} disconnected: {disconnectReason}");
+ Log($"Player {(serverPlayers.TryGetValue(id, out ServerPlayer player) ? player.Id : id)} disconnected: {disconnectReason}");
if (WorldStreamingInit.isLoaded)
SaveGameManager.Instance.UpdateInternalData();
@@ -1025,6 +1027,20 @@ private void OnServerboundTrainSyncRequestPacket(ServerboundTrainSyncRequestPack
networkedTrainCar.Server_DirtyAllState();
}
+ private void OnServerboundTrainCarSpawnRequest(ServerboundTrainCarSpawnRequest packet)
+ {
+ if (!TrainComponentLookup.Instance.LiveryFromId(packet.LiveryID, out TrainCarLivery livery))
+ {
+ NetworkLifecycle.Instance.Client.LogDebug(() => $"Tried spawning car but couldn't find TrainCarLivery with ID {packet.LiveryID}");
+ return;
+ }
+
+ Multiplayer.Log($"Client spawned {livery.name} car");
+ TrainCar trainCar = CarSpawner.Instance.SpawnCar(livery.prefab, RailTrack.GetClosest(packet.Position).track, packet.Position, packet.Forward, true);
+ NetworkedTrainCar networkedTrainCar = NetworkedCarSpawner.SpawnCar(TrainsetSpawnPart.FromTrainSet([trainCar])[0]);
+ NetworkLifecycle.Instance.Server.SendSpawnTrainCar(networkedTrainCar);
+ }
+
private void OnServerboundTrainDeleteRequestPacket(ServerboundTrainDeleteRequestPacket packet, ITransportPeer peer)
{
if (!TryGetServerPlayer(peer, out ServerPlayer player))
@@ -1176,7 +1192,7 @@ private void OnServerboundWarehouseMachineControllerRequestPacket(ServerboundWar
if(!NetworkedWarehouseMachineController.Get(packet.NetId, out var targetWarehouse))
{
LogWarning($"ServerboundWarehouseMachineControllerRequestPacket() WarehouseMachineController not found. NetId: {packet.NetId}");
- return;
+ return;
}
//Todo: add check for player distance from machine
diff --git a/Multiplayer/Networking/Packets/Serverbound/Train/ServerboundTrainCarSpawnRequest.cs b/Multiplayer/Networking/Packets/Serverbound/Train/ServerboundTrainCarSpawnRequest.cs
new file mode 100644
index 00000000..fbeccfcd
--- /dev/null
+++ b/Multiplayer/Networking/Packets/Serverbound/Train/ServerboundTrainCarSpawnRequest.cs
@@ -0,0 +1,23 @@
+using DV.ThingTypes;
+using UnityEngine;
+
+namespace Multiplayer.Networking.Packets.Serverbound.Train;
+
+public class ServerboundTrainCarSpawnRequest
+{
+ public Vector3 Position { get; set; }
+ public Vector3 Forward { get; set; }
+ public bool Derailed { get; set; }
+ public string LiveryID { get; set; }
+
+ public static ServerboundTrainCarSpawnRequest FromTrainCar(TrainCar trainCar)
+ {
+ return new ServerboundTrainCarSpawnRequest
+ {
+ Position = trainCar.transform.position,
+ Forward = trainCar.transform.forward,
+ Derailed = trainCar.derailed,
+ LiveryID = trainCar.carLivery.id,
+ };
+ }
+}
diff --git a/Multiplayer/Patches/CommsRadio/CommsRadioCarSpawnerPatch.cs b/Multiplayer/Patches/CommsRadio/CommsRadioCarSpawnerPatch.cs
deleted file mode 100644
index 092e5874..00000000
--- a/Multiplayer/Patches/CommsRadio/CommsRadioCarSpawnerPatch.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using System.Collections;
-using DV;
-using DV.InventorySystem;
-using HarmonyLib;
-using Multiplayer.Components.Networking;
-using Multiplayer.Components.Networking.Train;
-using Multiplayer.Utils;
-using UnityEngine;
-
-namespace Multiplayer.Patches.CommsRadio;
-
-
-[HarmonyPatch(typeof(CommsRadioCarSpawner))]
-public static class CommsRadioCarSpawnerPatch
-{
- [HarmonyPrefix]
- [HarmonyPatch(nameof(CommsRadioCarSpawner.OnUse))]
- private static bool OnUse_Prefix(CommsRadioCarSpawner __instance)
- {
- if (__instance.state != CommsRadioCarSpawner.State.PickDestination)
- return true;
- if (NetworkLifecycle.Instance.IsHost())
- return true;
-
- //temporarily disable client spawning
- CommsRadioController.PlayAudioFromRadio(__instance.cancelSound, __instance.transform);
- __instance.ClearFlags();
- return false;
-
- }
-}
-
- //private static IEnumerator PlaySoundsLater(CommsRadioCarDeleter __instance, Vector3 trainPosition, bool playMoneyRemovedSound = true)
- //{
- // yield return new WaitForSecondsRealtime((NetworkLifecycle.Instance.Client.Ping * 3f)/1000);
- // if (playMoneyRemovedSound && __instance.moneyRemovedSound != null)
- // __instance.moneyRemovedSound.Play2D();
- // // The TrainCar may already be deleted when we're done waiting, so we play the sound manually.
- // __instance.removeCarSound.Play(trainPosition, minDistance: CommsRadioController.CAR_AUDIO_SOURCE_MIN_DISTANCE, parent: WorldMover.Instance.originShiftParent);
- // CommsRadioController.PlayAudioFromRadio(__instance.confirmSound, __instance.transform);
- //}
-
-
diff --git a/Multiplayer/Patches/Train/CarSpawnerPatch.cs b/Multiplayer/Patches/Train/CarSpawnerPatch.cs
index 7aaad68f..e68fc0dd 100644
--- a/Multiplayer/Patches/Train/CarSpawnerPatch.cs
+++ b/Multiplayer/Patches/Train/CarSpawnerPatch.cs
@@ -3,6 +3,8 @@
using Multiplayer.Components.Networking.Train;
using Multiplayer.Utils;
using System.Collections.Generic;
+using DV.ThingTypes;
+using UnityEngine;
namespace Multiplayer.Patches.Train;
@@ -24,59 +26,88 @@ private static void PrepareTrainCarForDeleting(TrainCar trainCar)
NetworkLifecycle.Instance.Server?.SendDestroyTrainCar(networkedTrainCar);
}
- //Called from
- [HarmonyPatch(nameof(CarSpawner.SpawnCars))]
- [HarmonyPostfix]
- private static void SpawnCars(List __result)
- {
- if (UnloadWatcher.isUnloading)
- return;
-
- if (!NetworkLifecycle.Instance.IsHost())
- return;
-
- if (__result == null || __result.Count == 0)
- return;
-
- //Coupling is delayed by AutoCouple(), so a true trainset for the entire consist doesn't exist yet
- Multiplayer.LogDebug(() => $"SpawnCars() {__result?.Count} cars spawned, sending to players");
- NetworkLifecycle.Instance.Server.SendSpawnTrainset(__result, true, true);
-
- }
-
- [HarmonyPatch(nameof(CarSpawner.SpawnCarFromRemote))]
+ [HarmonyPatch(nameof(CarSpawner.SpawnCar))]
[HarmonyPostfix]
- private static void SpawnCarFromRemote(TrainCar __result)
+ private static void SpawnCar(TrainCar __result)
{
if (UnloadWatcher.isUnloading)
return;
if (!NetworkLifecycle.Instance.IsHost())
+ {
+ NetworkLifecycle.Instance.Client.SendTrainCarSpawnRequest(__result);
+ CarSpawner.Instance.DeleteCar(__result);
return;
+ }
if (__result == null)
return;
- Multiplayer.LogDebug(() => $"SpawnCarFromRemote() {__result?.carLivery?.name} spawned, sending to players");
+ //Coupling is delayed by AutoCouple(), so a true trainset for the entire consist doesn't exist yet
+ Multiplayer.LogDebug(() => $"SpawnCar() {__result?.carLivery?.name} car spawned, sending to players");
NetworkLifecycle.Instance.Server.SendSpawnTrainset([__result], true, true);
-
}
- [HarmonyPatch(nameof(CarSpawner.SpawnCarOnClosestTrack))]
+ [HarmonyPatch(nameof(CarSpawner.SpawnCars))]
[HarmonyPostfix]
- private static void SpawnCarOnClosestTrack(TrainCar __result)
+ private static void SpawnCars(List __result)
{
if (UnloadWatcher.isUnloading)
return;
if (!NetworkLifecycle.Instance.IsHost())
- return;
+ {
+ // todo: Create a SendTrainSetSpawnRequest packet and use that in these situations.
+ foreach (TrainCar trainCar in __result)
+ {
+ NetworkLifecycle.Instance.Client.SendTrainCarSpawnRequest(trainCar);
+ CarSpawner.Instance.DeleteCar(trainCar);
+ }
- if (__result == null)
return;
+ }
- Multiplayer.LogDebug(() => $"SpawnCarOnClosestTrack() {__result?.carLivery?.name} spawned, sending to players");
- NetworkLifecycle.Instance.Server.SendSpawnTrainset([__result], true, true);
+ if (__result == null || __result.Count == 0)
+ return;
+ //Coupling is delayed by AutoCouple(), so a true trainset for the entire consist doesn't exist yet
+ Multiplayer.LogDebug(() => $"SpawnCars() {__result?.Count} cars spawned, sending to players");
+ NetworkLifecycle.Instance.Server.SendSpawnTrainset(__result, true, true);
}
+
+ // [HarmonyPatch(nameof(CarSpawner.SpawnCarFromRemote))]
+ // [HarmonyPostfix]
+ // private static void SpawnCarFromRemote(TrainCar __result)
+ // {
+ // if (UnloadWatcher.isUnloading)
+ // return;
+ //
+ // if (!NetworkLifecycle.Instance.IsHost())
+ // return;
+ //
+ // if (__result == null)
+ // return;
+ //
+ // Multiplayer.LogDebug(() => $"SpawnCarFromRemote() {__result?.carLivery?.name} spawned, sending to players");
+ // NetworkLifecycle.Instance.Server.SendSpawnTrainset([__result], true, true);
+ //
+ // }
+ //
+ // [HarmonyPatch(nameof(CarSpawner.SpawnCarOnClosestTrack))]
+ // [HarmonyPostfix]
+ // private static void SpawnCarOnClosestTrack(TrainCar __result)
+ // {
+ // if (UnloadWatcher.isUnloading)
+ // return;
+ //
+ // if (!NetworkLifecycle.Instance.IsHost())
+ // return;
+ //
+ // if (__result == null)
+ // return;
+ //
+ // Multiplayer.LogDebug(() => $"SpawnCarOnClosestTrack() {__result?.carLivery?.name} spawned, sending to players");
+ // NetworkLifecycle.Instance.Server.SendSpawnTrainset([__result], true, true);
+ //
+ // }
}