Sync vehicle controls (ignition, engine, lights ...), stop forced starter sound, auto-reconnect after server restart or lost connection
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+7
-2
@@ -10,6 +10,8 @@ Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d
|
|||||||
- **Räder** drehen sich bei Autos, die ein anderer Spieler fährt
|
- **Räder** drehen sich bei Autos, die ein anderer Spieler fährt
|
||||||
- **Unfallschäden**: verbeulte Karosserie wird an alle übertragen
|
- **Unfallschäden**: verbeulte Karosserie wird an alle übertragen
|
||||||
- **Jobs, Aufträge und Laden**: Fortschritt (Auftrag angenommen, erledigt, bestellt ...) wird synchronisiert
|
- **Jobs, Aufträge und Laden**: Fortschritt (Auftrag angenommen, erledigt, bestellt ...) wird synchronisiert
|
||||||
|
- **Zündung, Motor, Licht, Choke, Blinker, Wischer**: die Schalterstellungen werden übertragen – beim anderen laufen
|
||||||
|
Motor und Scheinwerfer dadurch wirklich (nicht nur das Geräusch)
|
||||||
- **Autotüren, Motorhaube, Kofferraum**: stehen bei allen im selben Winkel offen
|
- **Autotüren, Motorhaube, Kofferraum**: stehen bei allen im selben Winkel offen
|
||||||
- **Verkehr**: NPC-Autos fahren bei allen an derselben Stelle (der erste Spieler simuliert, die anderen folgen)
|
- **Verkehr**: NPC-Autos fahren bei allen an derselben Stelle (der erste Spieler simuliert, die anderen folgen)
|
||||||
- `vehicles` in der F7-Konsole zeigt, wie viele Karosserie-Meshes, Job-Werte und Verkehrsautos synchronisiert werden
|
- `vehicles` in der F7-Konsole zeigt, wie viele Karosserie-Meshes, Job-Werte und Verkehrsautos synchronisiert werden
|
||||||
@@ -23,10 +25,13 @@ Server und Mod müssen beide 2.8 sein (Server: `git pull && docker compose up -d
|
|||||||
**Trennen & Neu-Verbinden**
|
**Trennen & Neu-Verbinden**
|
||||||
- Gekickt, gebannt, Server beendet, Verbindung verloren: eigenes Fenster in der Bildmitte mit **Grund** und OK
|
- Gekickt, gebannt, Server beendet, Verbindung verloren: eigenes Fenster in der Bildmitte mit **Grund** und OK
|
||||||
(im Spiel wird der Grund vorher 2,5 s groß angezeigt)
|
(im Spiel wird der Grund vorher 2,5 s groß angezeigt)
|
||||||
- `reset-world` / `reload-save`: Fenster „Reconnecting“ – verbindet automatisch neu, bis zu 5 Versuche (falls der
|
- `reset-world` / `reload-save` / **Server-Neustart** (z.B. Update per `docker compose up`) / **Verbindung verloren**:
|
||||||
Server gerade neu startet), mit CANCEL-Knopf
|
Fenster „Reconnecting“ – verbindet automatisch neu, bis zu 10 Versuche (ca. 1 Minute), mit CANCEL-Knopf.
|
||||||
|
Nach Kick, Ban oder eigenem Trennen wird nicht neu verbunden
|
||||||
|
|
||||||
**Behoben**
|
**Behoben**
|
||||||
|
- Beim anderen Spieler „orgelte“ der Anlasser endlos: erzwungene Geräusche/Lichter werden losgelassen, sobald keine
|
||||||
|
Daten mehr vom Fahrer kommen
|
||||||
- Beim Neuladen der Welt wurden herumliegende Objekte als „verbraucht“ gemeldet und bei allen gelöscht. Jetzt werden
|
- Beim Neuladen der Welt wurden herumliegende Objekte als „verbraucht“ gemeldet und bei allen gelöscht. Jetzt werden
|
||||||
nur einzelne echte Items gemeldet, Fahrzeuge/Teile nie gelöscht, und der Server nimmt höchstens 20 pro Minute an
|
nur einzelne echte Items gemeldet, Fahrzeuge/Teile nie gelöscht, und der Server nimmt höchstens 20 pro Minute an
|
||||||
|
|
||||||
|
|||||||
+167
@@ -0,0 +1,167 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
|
using HutongGames.PlayMaker;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace MSCO
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Bedienelemente der Fahrzeuge: Zündschloss, Anlasser, Motor, Lichtschalter, Choke, Blinker, Scheibenwischer ...
|
||||||
|
/// Das Spiel schaltet damit Motor und Licht über PlayMaker-FSMs (Zustände wie "Off" / "Lights" / "Running").
|
||||||
|
/// Übertragen wird der aktive Zustand; beim anderen Spieler wird dieselbe FSM in denselben Zustand geschaltet,
|
||||||
|
/// sodass sein Spiel Motor, Geräusch, Scheinwerfer und Armaturen selbst richtig anschaltet.
|
||||||
|
/// Wer sendet: der Fahrer; ist das Fahrzeug frei, wer daneben steht bzw. drin sitzt (Schalter betätigt).
|
||||||
|
/// FSMs, die ständig den Zustand wechseln (interne Schleifen), werden nach kurzer Zeit nicht mehr übertragen.
|
||||||
|
/// </summary>
|
||||||
|
public class ControlSync
|
||||||
|
{
|
||||||
|
// Objekt- oder FSM-Name enthält eines davon
|
||||||
|
static readonly string[] Keywords =
|
||||||
|
{
|
||||||
|
"ignition", "starter", "engine", "light", "choke", "wiper", "hazard", "indicator", "turnsignal", "blinker",
|
||||||
|
"horn", "heater", "fan", "radio", "key", "switch", "knob", "button"
|
||||||
|
};
|
||||||
|
// Montage/Speichern/Schrauben - läuft über den Objekt- bzw. FSM-Sync, hier nicht anfassen
|
||||||
|
static readonly string[] Excluded =
|
||||||
|
{
|
||||||
|
"removal", "assembly", "assemble", "bolt", "screw", "data", "save", "load", "paint", "detach", "attach",
|
||||||
|
"install", "trigger", "tighten", "wear", "damage", "oil", "coolant", "fuel", "inspection"
|
||||||
|
};
|
||||||
|
|
||||||
|
const float ScanInterval = 10f;
|
||||||
|
const float CheckInterval = 0.1f;
|
||||||
|
const float NearRange = 5f;
|
||||||
|
const int FlipLimit = 12; // so viele Zustandswechsel ...
|
||||||
|
const float FlipWindow = 10f; // ... in dieser Zeit = interne Schleife, nicht übertragen
|
||||||
|
const int MaxPerMessage = 100;
|
||||||
|
|
||||||
|
class Control
|
||||||
|
{
|
||||||
|
public int Key;
|
||||||
|
public string Root;
|
||||||
|
public PlayMakerFSM Fsm;
|
||||||
|
public string LastState;
|
||||||
|
public float WindowStart;
|
||||||
|
public int Flips;
|
||||||
|
public bool Muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly Dictionary<int, Control> controls = new Dictionary<int, Control>();
|
||||||
|
readonly HashSet<PlayMakerFSM> scanned = new HashSet<PlayMakerFSM>();
|
||||||
|
readonly List<Control> outgoing = new List<Control>();
|
||||||
|
float nextScan, nextCheck;
|
||||||
|
|
||||||
|
public int Count { get { return controls.Count; } }
|
||||||
|
public int MutedCount { get { int n = 0; foreach (Control c in controls.Values) if (c.Muted) n++; return n; } }
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
controls.Clear();
|
||||||
|
scanned.Clear();
|
||||||
|
nextScan = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(ObjectSync objects, int localId, Vector3 playerPos, Action<MsgType, byte[]> send)
|
||||||
|
{
|
||||||
|
if (Time.time >= nextScan)
|
||||||
|
{
|
||||||
|
nextScan = Time.time + ScanInterval;
|
||||||
|
Scan();
|
||||||
|
}
|
||||||
|
if (Time.time < nextCheck) return;
|
||||||
|
nextCheck = Time.time + CheckInterval;
|
||||||
|
|
||||||
|
outgoing.Clear();
|
||||||
|
foreach (Control c in controls.Values)
|
||||||
|
{
|
||||||
|
if (c.Fsm == null || c.Muted) continue;
|
||||||
|
string state = c.Fsm.ActiveStateName;
|
||||||
|
if (state == c.LastState || string.IsNullOrEmpty(state)) continue;
|
||||||
|
c.LastState = state;
|
||||||
|
|
||||||
|
if (Time.time - c.WindowStart > FlipWindow) { c.WindowStart = Time.time; c.Flips = 0; }
|
||||||
|
if (++c.Flips > FlipLimit)
|
||||||
|
{
|
||||||
|
c.Muted = true;
|
||||||
|
MscoLog.Info("[MSCO] Control not synced (changes all the time): " + c.Fsm.gameObject.name + " [" + c.Fsm.FsmName + "]");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int owner = objects.VehicleOwner(c.Root);
|
||||||
|
if (owner != SyncedBody.NoOwner && owner != localId) continue; // der Fahrer gibt vor
|
||||||
|
if (owner == SyncedBody.NoOwner && (c.Fsm.transform.position - playerPos).sqrMagnitude > NearRange * NearRange) continue;
|
||||||
|
outgoing.Add(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int start = 0; start < outgoing.Count; start += MaxPerMessage)
|
||||||
|
{
|
||||||
|
int from = start, count = Math.Min(MaxPerMessage, outgoing.Count - start);
|
||||||
|
send(MsgType.VehicleControls, NetSession.Build(w =>
|
||||||
|
{
|
||||||
|
w.Write(count);
|
||||||
|
for (int i = from; i < from + count; i++) { w.Write(outgoing[i].Key); w.Write(outgoing[i].LastState); }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(BinaryReader r, ObjectSync objects, int localId)
|
||||||
|
{
|
||||||
|
int count = r.ReadInt32();
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
int key = r.ReadInt32();
|
||||||
|
string state = r.ReadString();
|
||||||
|
Control c;
|
||||||
|
if (!controls.TryGetValue(key, out c) || c.Fsm == null) { nextScan = 0f; continue; }
|
||||||
|
if (objects.VehicleOwner(c.Root) == localId) continue; // ich fahre: meine Schalter gelten
|
||||||
|
if (c.Fsm.ActiveStateName == state) { c.LastState = state; continue; }
|
||||||
|
FsmState target = c.Fsm.Fsm.GetState(state);
|
||||||
|
if (target == null) continue;
|
||||||
|
c.LastState = state; // nicht als eigene Änderung zurückschicken
|
||||||
|
FsmSync.SwitchStateSilently(c.Fsm, target);
|
||||||
|
c.LastState = c.Fsm.ActiveStateName; // FSM kann sofort weiterschalten (z.B. "Start" -> "Running")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Scan()
|
||||||
|
{
|
||||||
|
int before = controls.Count;
|
||||||
|
foreach (PlayMakerFSM fsm in SceneCache.Fsms(5f))
|
||||||
|
{
|
||||||
|
if (fsm == null || !scanned.Add(fsm)) continue;
|
||||||
|
string root = fsm.transform.root.name;
|
||||||
|
if (!IsVehicle(root) || !Wanted(fsm.gameObject.name, fsm.FsmName)) continue;
|
||||||
|
int key = Hash(FsmSync.PathOf(fsm.transform) + "|" + fsm.FsmName);
|
||||||
|
if (controls.ContainsKey(key)) continue;
|
||||||
|
controls[key] = new Control { Key = key, Root = root, Fsm = fsm, LastState = fsm.ActiveStateName, WindowStart = Time.time };
|
||||||
|
}
|
||||||
|
if (controls.Count != before) MscoLog.Info("[MSCO] Vehicle controls (ignition, lights, engine ...): " + controls.Count + " synced");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Wanted(string objectName, string fsmName)
|
||||||
|
{
|
||||||
|
string o = objectName.ToLower(), f = fsmName.ToLower();
|
||||||
|
foreach (string x in Excluded) if (o.Contains(x) || f.Contains(x)) return false;
|
||||||
|
foreach (string k in Keywords) if (o.Contains(k) || f.Contains(k)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsVehicle(string root)
|
||||||
|
{
|
||||||
|
foreach (string v in ObjectSync.VehicleRoots) if (root.StartsWith(v)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Hash(string s)
|
||||||
|
{
|
||||||
|
unchecked
|
||||||
|
{
|
||||||
|
uint h = 2166136261;
|
||||||
|
foreach (byte c in Encoding.UTF8.GetBytes(s)) { h ^= c; h *= 16777619; }
|
||||||
|
return (int)h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -78,6 +78,7 @@
|
|||||||
<Compile Include="WorldVarSync.cs" />
|
<Compile Include="WorldVarSync.cs" />
|
||||||
<Compile Include="TrafficSync.cs" />
|
<Compile Include="TrafficSync.cs" />
|
||||||
<Compile Include="HingeSync.cs" />
|
<Compile Include="HingeSync.cs" />
|
||||||
|
<Compile Include="ControlSync.cs" />
|
||||||
<Compile Include="MpCommand.cs" />
|
<Compile Include="MpCommand.cs" />
|
||||||
<Compile Include="Commands.cs" />
|
<Compile Include="Commands.cs" />
|
||||||
<Compile Include="MscoLog.cs" />
|
<Compile Include="MscoLog.cs" />
|
||||||
|
|||||||
+24
-4
@@ -64,6 +64,7 @@ namespace MSCO
|
|||||||
readonly WorldVarSync worldVars = new WorldVarSync();
|
readonly WorldVarSync worldVars = new WorldVarSync();
|
||||||
readonly TrafficSync traffic = new TrafficSync();
|
readonly TrafficSync traffic = new TrafficSync();
|
||||||
readonly HingeSync hinges = new HingeSync();
|
readonly HingeSync hinges = new HingeSync();
|
||||||
|
readonly ControlSync controls = new ControlSync();
|
||||||
readonly Overlays overlays = new Overlays();
|
readonly Overlays overlays = new Overlays();
|
||||||
bool isAdmin; // Server hat uns als Admin gemeldet
|
bool isAdmin; // Server hat uns als Admin gemeldet
|
||||||
readonly HashSet<string> bannedNames = new HashSet<string>(); // P2P-Host: für diese Sitzung gebannt
|
readonly HashSet<string> bannedNames = new HashSet<string>(); // P2P-Host: für diese Sitzung gebannt
|
||||||
@@ -209,6 +210,7 @@ namespace MSCO
|
|||||||
lines.Add("synced vehicle values (fuel, oil, bolts, wear ...): " + vehicleState.Count);
|
lines.Add("synced vehicle values (fuel, oil, bolts, wear ...): " + vehicleState.Count);
|
||||||
lines.Add("synced body meshes (crash damage): " + damageSync.Count);
|
lines.Add("synced body meshes (crash damage): " + damageSync.Count);
|
||||||
lines.Add("synced car doors/hoods/trunks: " + hinges.Count);
|
lines.Add("synced car doors/hoods/trunks: " + hinges.Count);
|
||||||
|
lines.Add("synced vehicle controls (ignition, engine, lights ...): " + controls.Count + " (" + controls.MutedCount + " muted)");
|
||||||
lines.Add("synced job/shop values: " + worldVars.Count);
|
lines.Add("synced job/shop values: " + worldVars.Count);
|
||||||
lines.Add("synced traffic cars: " + traffic.Count + (IsWorldAuthority ? " (simulated here)" : " (following)"));
|
lines.Add("synced traffic cars: " + traffic.Count + (IsWorldAuthority ? " (simulated here)" : " (following)"));
|
||||||
return lines;
|
return lines;
|
||||||
@@ -607,7 +609,7 @@ namespace MSCO
|
|||||||
float centerMessageUntil;
|
float centerMessageUntil;
|
||||||
float reconnectAt;
|
float reconnectAt;
|
||||||
int reconnectAttempts;
|
int reconnectAttempts;
|
||||||
const int MaxReconnectAttempts = 5;
|
const int MaxReconnectAttempts = 10; // ~1 Minute: reicht für einen Server-Neustart
|
||||||
const float ReconnectRetrySeconds = 6f;
|
const float ReconnectRetrySeconds = 6f;
|
||||||
|
|
||||||
void ConnectToServer(string host, int port, string displayName, int maxPlayers)
|
void ConnectToServer(string host, int port, string displayName, int maxPlayers)
|
||||||
@@ -848,6 +850,8 @@ namespace MSCO
|
|||||||
{
|
{
|
||||||
reconnectAttempts++;
|
reconnectAttempts++;
|
||||||
reconnectAt = Time.realtimeSinceStartup + ReconnectRetrySeconds;
|
reconnectAt = Time.realtimeSinceStartup + ReconnectRetrySeconds;
|
||||||
|
MscoLog.Info("[MSCO] Reconnecting to " + lastServerHost + ":" + lastServerPort +
|
||||||
|
" (attempt " + reconnectAttempts + "/" + MaxReconnectAttempts + ")");
|
||||||
JoinServer(lastServerHost, lastServerPort, lastServerName, lastServerMax);
|
JoinServer(lastServerHost, lastServerPort, lastServerName, lastServerMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -990,6 +994,15 @@ namespace MSCO
|
|||||||
|
|
||||||
void OnSessionEnded(string reason)
|
void OnSessionEnded(string reason)
|
||||||
{
|
{
|
||||||
|
// Verbindung zum Server unerwartet weg (Absturz, Timeout, Netz) - nicht gekickt/gebannt, nicht selbst
|
||||||
|
// getrennt: automatisch neu verbinden, als hätte der Server einen Neustart angekündigt
|
||||||
|
if (reconnectMessage == null && serverReason == null && reason != "Disconnected" && dedicated && handshakeDone
|
||||||
|
&& lastServerHost != null)
|
||||||
|
{
|
||||||
|
reconnectMessage = "Lost the connection to the server - reconnecting...";
|
||||||
|
reconnectAttempts = 0;
|
||||||
|
MscoLog.Warning("[MSCO] Connection lost (" + reason + ") - will reconnect");
|
||||||
|
}
|
||||||
// Hinweis fürs Hauptmenü - außer man hat selbst auf DISCONNECT gedrückt oder verbindet gleich neu
|
// Hinweis fürs Hauptmenü - außer man hat selbst auf DISCONNECT gedrückt oder verbindet gleich neu
|
||||||
if (reconnectMessage != null)
|
if (reconnectMessage != null)
|
||||||
{
|
{
|
||||||
@@ -1022,7 +1035,7 @@ namespace MSCO
|
|||||||
worldSync.Reset();
|
worldSync.Reset();
|
||||||
moneySync.Reset();
|
moneySync.Reset();
|
||||||
doorSync.Reset();
|
doorSync.Reset();
|
||||||
vehicleState.Reset(); damageSync.Reset(); worldVars.Reset(); traffic.Reset(); hinges.Reset();
|
vehicleState.Reset(); damageSync.Reset(); worldVars.Reset(); traffic.Reset(); hinges.Reset(); controls.Reset();
|
||||||
isAdmin = false;
|
isAdmin = false;
|
||||||
if (overlays.AdminOpen || overlays.ChatOpen) overlays.CloseAll();
|
if (overlays.AdminOpen || overlays.ChatOpen) overlays.CloseAll();
|
||||||
FsmSync.Enabled = false;
|
FsmSync.Enabled = false;
|
||||||
@@ -1051,7 +1064,8 @@ namespace MSCO
|
|||||||
|| t == MsgType.ObjRelease || t == MsgType.FsmEvent || t == MsgType.VehicleFx || t == MsgType.WorldTime || t == MsgType.MoneyDelta || t == MsgType.DoorState
|
|| t == MsgType.ObjRelease || t == MsgType.FsmEvent || t == MsgType.VehicleFx || t == MsgType.WorldTime || t == MsgType.MoneyDelta || t == MsgType.DoorState
|
||||||
|| t == MsgType.Chat || t == MsgType.VarSync || t == MsgType.SpawnItem
|
|| t == MsgType.Chat || t == MsgType.VarSync || t == MsgType.SpawnItem
|
||||||
|| t == MsgType.ItemAppeared || t == MsgType.ObjDestroy || t == MsgType.VehicleDamage
|
|| t == MsgType.ItemAppeared || t == MsgType.ObjDestroy || t == MsgType.VehicleDamage
|
||||||
|| t == MsgType.TrafficStates || t == MsgType.WorldVars || t == MsgType.HingeStates;
|
|| t == MsgType.TrafficStates || t == MsgType.WorldVars || t == MsgType.HingeStates
|
||||||
|
|| t == MsgType.VehicleControls;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnMessage(int peerId, Message m)
|
void OnMessage(int peerId, Message m)
|
||||||
@@ -1366,6 +1380,9 @@ namespace MSCO
|
|||||||
case MsgType.HingeStates:
|
case MsgType.HingeStates:
|
||||||
hinges.Handle(r);
|
hinges.Handle(r);
|
||||||
break;
|
break;
|
||||||
|
case MsgType.VehicleControls:
|
||||||
|
controls.Handle(r, objects, localId);
|
||||||
|
break;
|
||||||
case MsgType.TrafficStates:
|
case MsgType.TrafficStates:
|
||||||
traffic.Handle(r, IsWorldAuthority);
|
traffic.Handle(r, IsWorldAuthority);
|
||||||
break;
|
break;
|
||||||
@@ -1486,7 +1503,7 @@ namespace MSCO
|
|||||||
passenger.Reset();
|
passenger.Reset();
|
||||||
localPlayer = null;
|
localPlayer = null;
|
||||||
objects.OnGameLoaded();
|
objects.OnGameLoaded();
|
||||||
vehicleState.Reset(); damageSync.Reset(); worldVars.Reset(); traffic.Reset(); hinges.Reset();
|
vehicleState.Reset(); damageSync.Reset(); worldVars.Reset(); traffic.Reset(); hinges.Reset(); controls.Reset();
|
||||||
vehicleFx.Reset(); wheelSpin.Reset();
|
vehicleFx.Reset(); wheelSpin.Reset();
|
||||||
worldSync.Reset();
|
worldSync.Reset();
|
||||||
moneySync.Reset();
|
moneySync.Reset();
|
||||||
@@ -1610,6 +1627,9 @@ namespace MSCO
|
|||||||
// Unfallschäden (verbeulte Karosserie)
|
// Unfallschäden (verbeulte Karosserie)
|
||||||
try { if (objects.ScanComplete) damageSync.Update(objects, localId, SendGame); }
|
try { if (objects.ScanComplete) damageSync.Update(objects, localId, SendGame); }
|
||||||
catch (System.Exception e) { ReportError("crash damage sync", e); }
|
catch (System.Exception e) { ReportError("crash damage sync", e); }
|
||||||
|
// Zündung, Motor, Licht, Choke ... der Fahrzeuge
|
||||||
|
try { if (objects.ScanComplete && localPlayer != null) controls.Update(objects, localId, localPlayer.position, SendGame); }
|
||||||
|
catch (System.Exception e) { ReportError("vehicle controls sync", e); }
|
||||||
// Türen, Motorhaube, Kofferraum der Fahrzeuge
|
// Türen, Motorhaube, Kofferraum der Fahrzeuge
|
||||||
try { if (objects.ScanComplete && localPlayer != null) hinges.Update(objects, localId, localPlayer.position, Time.deltaTime, SendGame); }
|
try { if (objects.ScanComplete && localPlayer != null) hinges.Update(objects, localId, localPlayer.position, Time.deltaTime, SendGame); }
|
||||||
catch (System.Exception e) { ReportError("car door sync", e); }
|
catch (System.Exception e) { ReportError("car door sync", e); }
|
||||||
|
|||||||
+2
-1
@@ -50,7 +50,8 @@ namespace MSCO
|
|||||||
VehicleDamage = 39, // Unfallschaden: int Mesh-Schlüssel, int Eckpunkte gesamt, int n, je [int Index, 3 float]
|
VehicleDamage = 39, // Unfallschaden: int Mesh-Schlüssel, int Eckpunkte gesamt, int n, je [int Index, 3 float]
|
||||||
TrafficStates = 40, // Verkehr (nur Welt-Autorität): int n, je [int Schlüssel, 3 float Pos, 4 float Rot, 3 float Geschw.]
|
TrafficStates = 40, // Verkehr (nur Welt-Autorität): int n, je [int Schlüssel, 3 float Pos, 4 float Rot, 3 float Geschw.]
|
||||||
WorldVars = 41, // Jobs/Laden: int n, je [int Schlüssel, byte Typ (2 int, 3 bool), Wert]
|
WorldVars = 41, // Jobs/Laden: int n, je [int Schlüssel, byte Typ (2 int, 3 bool), Wert]
|
||||||
HingeStates = 42 // Autotüren/Haube/Kofferraum: int n, je [int Schlüssel, 3 float Pos, 4 float Rot relativ zum Auto]
|
HingeStates = 42, // Autotüren/Haube/Kofferraum: int n, je [int Schlüssel, 3 float Pos, 4 float Rot relativ zum Auto]
|
||||||
|
VehicleControls = 43 // Zündung/Motor/Licht ...: int n, je [int FSM-Schlüssel, string Zustand]
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Message
|
public struct Message
|
||||||
|
|||||||
+28
-1
@@ -46,6 +46,7 @@ namespace MSCO
|
|||||||
class Snapshot
|
class Snapshot
|
||||||
{
|
{
|
||||||
public float ReceivedAt;
|
public float ReceivedAt;
|
||||||
|
public bool Released; // nach dem Timeout einmal freigegeben
|
||||||
public readonly Dictionary<int, bool> Lights = new Dictionary<int, bool>();
|
public readonly Dictionary<int, bool> Lights = new Dictionary<int, bool>();
|
||||||
public readonly Dictionary<int, AudioState> Audio = new Dictionary<int, AudioState>();
|
public readonly Dictionary<int, AudioState> Audio = new Dictionary<int, AudioState>();
|
||||||
public readonly Dictionary<int, Quaternion> Dials = new Dictionary<int, Quaternion>();
|
public readonly Dictionary<int, Quaternion> Dials = new Dictionary<int, Quaternion>();
|
||||||
@@ -161,11 +162,17 @@ namespace MSCO
|
|||||||
{
|
{
|
||||||
foreach (KeyValuePair<int, Snapshot> kv in incoming)
|
foreach (KeyValuePair<int, Snapshot> kv in incoming)
|
||||||
{
|
{
|
||||||
if (Time.time - kv.Value.ReceivedAt > ApplyTimeout) continue;
|
|
||||||
SyncedBody b = objects.Get(kv.Key);
|
SyncedBody b = objects.Get(kv.Key);
|
||||||
if (b == null || objects.IsLocalBody(b)) continue; // eigene Fahrzeuge simulieren wir selbst
|
if (b == null || objects.IsLocalBody(b)) continue; // eigene Fahrzeuge simulieren wir selbst
|
||||||
Components c = GetComponents(b);
|
Components c = GetComponents(b);
|
||||||
if (c == null) continue;
|
if (c == null) continue;
|
||||||
|
if (Time.time - kv.Value.ReceivedAt > ApplyTimeout)
|
||||||
|
{
|
||||||
|
// Keine Daten mehr (Fahrer ausgestiegen o.ä.): einmal alles loslassen, was wir erzwungen haben -
|
||||||
|
// sonst läuft z.B. das Anlasser-Geräusch endlos weiter ("orgelt")
|
||||||
|
if (!kv.Value.Released) Release(kv.Value, c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (KeyValuePair<int, bool> l in kv.Value.Lights)
|
foreach (KeyValuePair<int, bool> l in kv.Value.Lights)
|
||||||
{
|
{
|
||||||
@@ -192,6 +199,26 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Release(Snapshot s, Components c)
|
||||||
|
{
|
||||||
|
s.Released = true;
|
||||||
|
foreach (KeyValuePair<int, AudioState> a in s.Audio)
|
||||||
|
{
|
||||||
|
AudioSource src;
|
||||||
|
if (a.Value.Playing && c.Audio.TryGetValue(a.Key, out src) && src != null && src.isPlaying) src.Stop();
|
||||||
|
}
|
||||||
|
foreach (KeyValuePair<int, bool> p in s.Particles)
|
||||||
|
{
|
||||||
|
ParticleSystem ps;
|
||||||
|
if (p.Value && c.Particles.TryGetValue(p.Key, out ps) && ps != null && ps.isPlaying) ps.Stop();
|
||||||
|
}
|
||||||
|
foreach (KeyValuePair<int, bool> l in s.Lights)
|
||||||
|
{
|
||||||
|
Light light;
|
||||||
|
if (l.Value && c.Lights.TryGetValue(l.Key, out light) && light != null) light.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void SetLight(Light light, bool on, Transform root)
|
static void SetLight(Light light, bool on, Transform root)
|
||||||
{
|
{
|
||||||
if (!on)
|
if (!on)
|
||||||
|
|||||||
Reference in New Issue
Block a user