using System; using System.Collections.Generic; using System.IO; using HutongGames.PlayMaker; using MSCLoader; using UnityEngine; namespace MSCO { /// /// Uhrzeit und Wetter. Ein Spieler ist "Weltuhr" (P2P: der Host; dedizierter Server: der Spieler /// mit der kleinsten ID) und schickt regelmäßig den Zustand der Uhr- und Wetter-FSMs /// (aktiver Zustand + alle Zahlen-/Ja-Nein-Variablen + Drehung, z.B. der Sonne) sowie globale /// Werte wie den Wochentag. Alle anderen übernehmen diesen Zustand. /// public class WorldSync { // FSMs, die Uhrzeit und Wetter steuern static readonly string[] TrackedFsmNames = { "Clock", "Weather" }; // Globale PlayMaker-Variablen (Wochentag, Tageszeit) static readonly string[] TrackedGlobals = { "GlobalDay", "TimeOfDay" }; const float SendInterval = 2f; const float DiscoverRetry = 5f; class Tracked { public string Key; public PlayMakerFSM Fsm; } readonly Dictionary tracked = new Dictionary(); float sendTimer; float nextDiscover; bool discovered; public int TrackedCount { get { return tracked.Count; } } public void Reset() { tracked.Clear(); discovered = false; nextDiscover = 0f; } // ---------------------------------------------------------------- Senden (Weltuhr) public void Update(bool isAuthority, Action send, float dt) { if (!discovered && Time.time >= nextDiscover) Discover(); if (!isAuthority || tracked.Count == 0) return; sendTimer += dt; if (sendTimer < SendInterval) return; sendTimer = 0f; send(MsgType.WorldTime, BuildState()); } byte[] BuildState() { var alive = new List(); foreach (Tracked t in tracked.Values) if (t.Fsm != null) alive.Add(t); return NetSession.Build(w => { w.Write(alive.Count); foreach (Tracked t in alive) { w.Write(t.Key); w.Write(t.Fsm.ActiveStateName ?? ""); Quaternion rot = t.Fsm.transform.localRotation; w.Write(rot.x); w.Write(rot.y); w.Write(rot.z); w.Write(rot.w); WriteVariables(w, t.Fsm.FsmVariables, null); } WriteVariables(w, FsmVariables.GlobalVariables, TrackedGlobals); }); } static void WriteVariables(BinaryWriter w, FsmVariables vars, string[] only) { var floats = Filter(vars.FloatVariables, only); w.Write(floats.Count); foreach (FsmFloat v in floats) { w.Write(v.Name); w.Write(v.Value); } var ints = Filter(vars.IntVariables, only); w.Write(ints.Count); foreach (FsmInt v in ints) { w.Write(v.Name); w.Write(v.Value); } var bools = Filter(vars.BoolVariables, only); w.Write(bools.Count); foreach (FsmBool v in bools) { w.Write(v.Name); w.Write(v.Value); } } static List Filter(T[] vars, string[] only) where T : NamedVariable { var list = new List(); if (vars == null) return list; foreach (T v in vars) if (v != null && !string.IsNullOrEmpty(v.Name) && (only == null || Array.IndexOf(only, v.Name) >= 0)) list.Add(v); return list; } // ---------------------------------------------------------------- Empfangen public void Apply(BinaryReader r) { if (!discovered) Discover(); int count = r.ReadInt32(); for (int i = 0; i < count; i++) { string key = r.ReadString(); string state = r.ReadString(); var rot = new Quaternion(r.ReadSingle(), r.ReadSingle(), r.ReadSingle(), r.ReadSingle()); Tracked t; PlayMakerFSM fsm = tracked.TryGetValue(key, out t) ? t.Fsm : null; ReadVariables(r, fsm != null ? fsm.FsmVariables : null); if (fsm == null) continue; fsm.transform.localRotation = rot; if (!string.IsNullOrEmpty(state) && fsm.ActiveStateName != state) { FsmState target = fsm.Fsm.GetState(state); if (target != null) FsmSync.SwitchStateSilently(fsm, target); // z.B. Regen beginnt/hört auf } } ReadVariables(r, FsmVariables.GlobalVariables); } static void ReadVariables(BinaryReader r, FsmVariables vars) { int floats = r.ReadInt32(); for (int i = 0; i < floats; i++) { string name = r.ReadString(); float value = r.ReadSingle(); FsmFloat v = vars != null ? vars.FindFsmFloat(name) : null; if (v != null) v.Value = value; } int ints = r.ReadInt32(); for (int i = 0; i < ints; i++) { string name = r.ReadString(); int value = r.ReadInt32(); FsmInt v = vars != null ? vars.FindFsmInt(name) : null; if (v != null) v.Value = value; } int bools = r.ReadInt32(); for (int i = 0; i < bools; i++) { string name = r.ReadString(); bool value = r.ReadBoolean(); FsmBool v = vars != null ? vars.FindFsmBool(name) : null; if (v != null) v.Value = value; } } // ---------------------------------------------------------------- Suchen void Discover() { nextDiscover = Time.time + DiscoverRetry; tracked.Clear(); foreach (PlayMakerFSM fsm in SceneCache.Fsms(5f)) { if (fsm == null) continue; if (Array.IndexOf(TrackedFsmNames, fsm.FsmName) < 0) continue; string root = fsm.transform.root.name; if (root == "PLAYER" || root.StartsWith("MSCO_")) continue; string key = FsmSync.PathOf(fsm.transform) + "|" + fsm.FsmName; if (!tracked.ContainsKey(key)) tracked[key] = new Tracked { Key = key, Fsm = fsm }; } if (tracked.Count == 0) return; // Welt noch nicht fertig geladen - später nochmal discovered = true; MscoLog.Info("[MSCO] Time/weather sync: " + tracked.Count + " FSMs"); if (FsmSync.Debug) foreach (string key in tracked.Keys) MscoLog.Info("[MSCO] " + key); } } }