using System; using System.Collections.Generic; using System.IO; using System.Text; using HutongGames.PlayMaker; using UnityEngine; namespace MSCO { /// /// Jobs, Aufträge und Laden: deren Fortschritt steht in PlayMaker-FSMs (z.B. "Auftrag angenommen", Anzahl /// geleerter Klärgruben, bestellte Teile). Übertragen werden nur Ganzzahlen und Schalter - keine Kommazahlen /// (Timer, Positionen), die jeder selbst weiterzählt. Wer etwas ändert, schickt es; Werte, die ständig hin und her /// springen (interne Zähler), werden nach kurzer Zeit nicht mehr übertragen. /// public class WorldVarSync { /// Wurzelobjekte mit Jobs/Aufträgen/Laden. public static readonly string[] Roots = { "JOBS", "STORE", "REPAIRSHOP", "INSPECTION", "LANDFILL", "DANCEHALL", "COTTAGE", "RALLY" }; const float ScanInterval = 15f; const float CheckInterval = 0.5f; const float MinResend = 2f; // höchstens so oft pro Wert const int FlipLimit = 6; // so viele Änderungen ... const float FlipWindow = 30f; // ... in dieser Zeit = interner Zähler, nicht mehr übertragen const int MaxPerMessage = 200; class Var { public int Key; public NamedVariable Variable; public byte Type; // 2 int, 3 bool public int LastI; public bool LastB; public float LastSent = -100f; public float WindowStart; public int Flips; public bool Muted; } readonly Dictionary vars = new Dictionary(); readonly HashSet scanned = new HashSet(); readonly List outgoing = new List(); float nextScan, nextCheck; public int Count { get { return vars.Count; } } public void Reset() { vars.Clear(); scanned.Clear(); nextScan = -1f; // erster Suchlauf verteilt, siehe Update } public void Update(Action send) { if (nextScan < 0f) nextScan = Time.time + 3.8f; // nicht alle Sync-Teile im selben Frame suchen lassen if (Time.time >= nextScan) { nextScan = Time.time + ScanInterval; Scan(); } if (Time.time < nextCheck) return; nextCheck = Time.time + CheckInterval; outgoing.Clear(); foreach (Var v in vars.Values) { if (v.Muted || !Changed(v)) continue; if (Time.time - v.LastSent < MinResend) continue; // später nochmal prüfen Remember(v); if (Time.time - v.WindowStart > FlipWindow) { v.WindowStart = Time.time; v.Flips = 0; } if (++v.Flips > FlipLimit) { v.Muted = true; continue; } v.LastSent = Time.time; outgoing.Add(v); } for (int start = 0; start < outgoing.Count; start += MaxPerMessage) { int from = start, count = Math.Min(MaxPerMessage, outgoing.Count - start); send(MsgType.WorldVars, NetSession.Build(w => { w.Write(count); for (int i = from; i < from + count; i++) { Var v = outgoing[i]; w.Write(v.Key); w.Write(v.Type); if (v.Type == 2) w.Write(v.LastI); else w.Write(v.LastB); } })); } } public void Handle(BinaryReader r) { int count = r.ReadInt32(); for (int i = 0; i < count; i++) { int key = r.ReadInt32(); byte type = r.ReadByte(); int iv = 0; bool bv = false; if (type == 2) iv = r.ReadInt32(); else if (type == 3) bv = r.ReadBoolean(); else return; Var v; if (!vars.TryGetValue(key, out v) || v.Type != type) continue; if (type == 2) ((FsmInt)v.Variable).Value = iv; else ((FsmBool)v.Variable).Value = bv; Remember(v); // nicht zurückschicken } } void Scan() { int before = vars.Count; foreach (PlayMakerFSM fsm in SceneCache.Fsms(5f)) { if (fsm == null || !scanned.Add(fsm)) continue; if (Array.IndexOf(Roots, fsm.transform.root.name) < 0) continue; string prefix = FsmSync.PathOf(fsm.transform) + "|" + fsm.FsmName + "|"; FsmVariables fv = fsm.FsmVariables; foreach (FsmInt n in fv.IntVariables) Register(prefix, n, 2); foreach (FsmBool b in fv.BoolVariables) Register(prefix, b, 3); } if (vars.Count != before) MscoLog.Info("[MSCO] Jobs/shop: " + vars.Count + " values synced"); } void Register(string prefix, NamedVariable variable, byte type) { if (variable == null || string.IsNullOrEmpty(variable.Name)) return; int key = Hash(prefix + variable.Name); if (vars.ContainsKey(key)) return; var v = new Var { Key = key, Variable = variable, Type = type }; Remember(v); vars[key] = v; } static bool Changed(Var v) { return v.Type == 2 ? ((FsmInt)v.Variable).Value != v.LastI : ((FsmBool)v.Variable).Value != v.LastB; } static void Remember(Var v) { if (v.Type == 2) v.LastI = ((FsmInt)v.Variable).Value; else v.LastB = ((FsmBool)v.Variable).Value; } static int Hash(string s) { unchecked { uint h = 2166136261; foreach (byte c in Encoding.UTF8.GetBytes(s)) { h ^= c; h *= 16777619; } return (int)h; } } } }