157 lines
5.9 KiB
C#
157 lines
5.9 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using HutongGames.PlayMaker;
|
|
using UnityEngine;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public class WorldVarSync
|
|
{
|
|
/// <summary>Wurzelobjekte mit Jobs/Aufträgen/Laden.</summary>
|
|
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<int, Var> vars = new Dictionary<int, Var>();
|
|
readonly HashSet<PlayMakerFSM> scanned = new HashSet<PlayMakerFSM>();
|
|
readonly List<Var> outgoing = new List<Var>();
|
|
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<MsgType, byte[]> 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(20f))
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|