2.8: sync bought/consumed items, wheel spin, crash damage, jobs/shop, traffic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+182
@@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>
|
||||
/// Unfallschäden (verbeulte Karosserie): Das Spiel verformt die Meshes der Fahrzeuge bei Kollisionen
|
||||
/// (CarDamage, CarDamage2, Deformable). Das lässt sich nicht nachspielen, also übertragen wir das Ergebnis:
|
||||
/// Wer das Fahrzeug fährt (oder bei freien Fahrzeugen: bei wem es passiert), schickt die geänderten Eckpunkte.
|
||||
/// </summary>
|
||||
public class DamageSync
|
||||
{
|
||||
const float ScanInterval = 10f;
|
||||
const float CheckInterval = 1f;
|
||||
const float MinMove = 0.002f;
|
||||
const int MaxPerMessage = 4000;
|
||||
|
||||
static readonly string[] DamageTypes = { "CarDamage", "CarDamage2", "Deformable" };
|
||||
|
||||
class DamageMesh
|
||||
{
|
||||
public int Key;
|
||||
public string Root;
|
||||
public MeshFilter Filter;
|
||||
public Vector3[] Known; // zuletzt gesendet/empfangen
|
||||
}
|
||||
|
||||
readonly Dictionary<int, DamageMesh> meshes = new Dictionary<int, DamageMesh>();
|
||||
readonly HashSet<MeshFilter> seen = new HashSet<MeshFilter>();
|
||||
readonly List<int> changed = new List<int>();
|
||||
float nextScan, nextCheck;
|
||||
static List<Type> types;
|
||||
|
||||
public int Count { get { return meshes.Count; } }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
meshes.Clear();
|
||||
seen.Clear();
|
||||
nextScan = 0f;
|
||||
}
|
||||
|
||||
public void Update(ObjectSync objects, int localId, Action<MsgType, byte[]> send)
|
||||
{
|
||||
if (Time.time >= nextScan)
|
||||
{
|
||||
nextScan = Time.time + ScanInterval;
|
||||
Scan();
|
||||
}
|
||||
if (Time.time < nextCheck) return;
|
||||
nextCheck = Time.time + CheckInterval;
|
||||
|
||||
foreach (DamageMesh m in meshes.Values)
|
||||
{
|
||||
if (m.Filter == null) continue;
|
||||
int owner = objects.VehicleOwner(m.Root);
|
||||
if (owner != SyncedBody.NoOwner && owner != localId) continue; // der Fahrer schickt die Schäden
|
||||
|
||||
Mesh mesh = m.Filter.sharedMesh;
|
||||
if (mesh == null) continue;
|
||||
Vector3[] now = mesh.vertices;
|
||||
if (now.Length != m.Known.Length) { m.Known = now; continue; }
|
||||
|
||||
changed.Clear();
|
||||
for (int i = 0; i < now.Length; i++)
|
||||
if ((now[i] - m.Known[i]).sqrMagnitude > MinMove * MinMove) changed.Add(i);
|
||||
if (changed.Count == 0) continue;
|
||||
|
||||
for (int start = 0; start < changed.Count; start += MaxPerMessage)
|
||||
{
|
||||
int from = start, count = Math.Min(MaxPerMessage, changed.Count - start);
|
||||
int key = m.Key, total = now.Length;
|
||||
send(MsgType.VehicleDamage, NetSession.Build(w =>
|
||||
{
|
||||
w.Write(key);
|
||||
w.Write(total);
|
||||
w.Write(count);
|
||||
for (int i = from; i < from + count; i++)
|
||||
{
|
||||
int idx = changed[i];
|
||||
Vector3 v = now[idx];
|
||||
w.Write(idx); w.Write(v.x); w.Write(v.y); w.Write(v.z);
|
||||
}
|
||||
}));
|
||||
}
|
||||
m.Known = now;
|
||||
}
|
||||
}
|
||||
|
||||
public void Handle(BinaryReader r)
|
||||
{
|
||||
int key = r.ReadInt32();
|
||||
int total = r.ReadInt32();
|
||||
int count = r.ReadInt32();
|
||||
DamageMesh m;
|
||||
if (!meshes.TryGetValue(key, out m) || m.Filter == null) { nextScan = 0f; return; }
|
||||
Mesh mesh = m.Filter.mesh; // eigene Kopie, falls das Mesh (noch) mit anderen geteilt ist
|
||||
if (mesh == null) return;
|
||||
Vector3[] verts = mesh.vertices;
|
||||
if (verts.Length != total) return; // anderes Mesh (anderes Teil verbaut)
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int idx = r.ReadInt32();
|
||||
var v = new Vector3(r.ReadSingle(), r.ReadSingle(), r.ReadSingle());
|
||||
if (idx < 0 || idx >= verts.Length || float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z)) continue;
|
||||
if (v.sqrMagnitude > 1e6f) continue; // Unsinn nie übernehmen
|
||||
verts[idx] = v;
|
||||
}
|
||||
mesh.vertices = verts;
|
||||
mesh.RecalculateNormals();
|
||||
mesh.RecalculateBounds();
|
||||
m.Known = verts;
|
||||
}
|
||||
|
||||
void Scan()
|
||||
{
|
||||
if (types == null)
|
||||
{
|
||||
types = new List<Type>();
|
||||
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (a.GetName().Name != "Assembly-CSharp") continue;
|
||||
foreach (string n in DamageTypes)
|
||||
{
|
||||
Type t = a.GetType(n);
|
||||
if (t != null) types.Add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int before = meshes.Count;
|
||||
foreach (Type t in types)
|
||||
{
|
||||
foreach (UnityEngine.Object o in UnityEngine.Object.FindObjectsOfType(t))
|
||||
{
|
||||
var c = o as Component;
|
||||
if (c == null || !IsVehicle(c.transform.root.name)) continue;
|
||||
foreach (FieldInfo f in t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||
{
|
||||
if (f.FieldType == typeof(MeshFilter)) Register(f.GetValue(c) as MeshFilter);
|
||||
else if (f.FieldType == typeof(MeshFilter[]))
|
||||
{
|
||||
var arr = f.GetValue(c) as MeshFilter[];
|
||||
if (arr != null) foreach (MeshFilter mf in arr) Register(mf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (meshes.Count != before) MscoLog.Info("[MSCO] Crash damage: " + meshes.Count + " body meshes synced");
|
||||
}
|
||||
|
||||
void Register(MeshFilter mf)
|
||||
{
|
||||
if (mf == null || !seen.Add(mf)) return;
|
||||
Mesh mesh = mf.sharedMesh;
|
||||
if (mesh == null || mesh.vertexCount == 0 || mesh.vertexCount > 65000) return;
|
||||
int key = Hash(FsmSync.PathOf(mf.transform));
|
||||
if (meshes.ContainsKey(key)) return; // nicht eindeutig
|
||||
meshes[key] = new DamageMesh { Key = key, Root = mf.transform.root.name, Filter = mf, Known = mesh.vertices };
|
||||
}
|
||||
|
||||
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 (char c in s) { h ^= c; h *= 16777619; }
|
||||
return (int)h;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user