mirror of
https://github.com/riperiperi/FreeSO
synced 2026-08-13 20:26:13 -04:00
135 lines
5 KiB
C#
135 lines
5 KiB
C#
using FSO.SimAntics.NetPlay.Model;
|
|
using FSO.SimAntics.NetPlay.Model.Commands;
|
|
using FSO.SimAntics.Engine.TSOTransaction;
|
|
using System.IO;
|
|
using FSO.SimAntics.NetPlay.Drivers;
|
|
|
|
namespace FSO.SimAntics.NetPlay
|
|
{
|
|
public abstract class VMNetDriver
|
|
{
|
|
public IVMTSOGlobalLink GlobalLink;
|
|
public abstract void SendCommand(VMNetCommandBodyAbstract cmd);
|
|
public abstract void SendDirectCommand(uint pid, VMNetCommandBodyAbstract cmd);
|
|
public abstract bool Tick(VM vm);
|
|
public abstract string GetUserIP(uint uid);
|
|
public VMCloseNetReason CloseReason;
|
|
public string CloseString;
|
|
private BinaryWriter RecordStream;
|
|
public VMNetCommand Executing;
|
|
|
|
/// <summary>
|
|
/// Indicates a VM inspired total connection shutdown.
|
|
/// </summary>
|
|
public event VMNetClosedHandler OnShutdown;
|
|
public delegate void VMNetClosedHandler(VMCloseNetReason reason);
|
|
|
|
public uint DesyncTick = 0;
|
|
private int DesyncCooldown = 30;
|
|
public uint LastTick = 0;
|
|
public uint GreatestTick = 0;
|
|
public uint CurrentTick = 0;
|
|
public bool InResync
|
|
{
|
|
get
|
|
{
|
|
//if the current tick is less than our greatest tick
|
|
//then the client has already seen this tick.
|
|
return GreatestTick > CurrentTick;
|
|
}
|
|
}
|
|
|
|
public bool AsyncBreak; //if
|
|
|
|
protected void InternalTick(VM vm, VMNetTick tick)
|
|
{
|
|
CurrentTick = tick.TickID;
|
|
if (CurrentTick > GreatestTick) GreatestTick = CurrentTick;
|
|
if (!tick.ImmediateMode && (tick.Commands.Count == 0 || !(tick.Commands[0].Command is VMStateSyncCmd)) && vm.Context.RandomSeed != tick.RandomSeed)
|
|
{
|
|
if (DesyncCooldown == 0)
|
|
{
|
|
System.Console.WriteLine("DESYNC - Requested state from host");
|
|
if (DesyncTick == 0) DesyncTick = CurrentTick - 1;
|
|
vm.SendCommand(new VMRequestResyncCmd());
|
|
DesyncCooldown = 30 * 30;
|
|
}
|
|
else
|
|
{
|
|
System.Console.WriteLine("WARN - DESYNC - Expected " + tick.RandomSeed + ", was at " + vm.Context.RandomSeed);
|
|
}
|
|
}
|
|
|
|
if (RecordStream != null) RecordTick(tick);
|
|
vm.Context.RandomSeed = tick.RandomSeed;
|
|
AsyncBreak = false;
|
|
bool doTick = !tick.ImmediateMode;
|
|
foreach(var cmd in tick.Commands)
|
|
{
|
|
if (cmd.Command is VMStateSyncCmd && ((VMStateSyncCmd)cmd.Command).Run)
|
|
{
|
|
if (LastTick + 1 != tick.TickID) System.Console.WriteLine("Jump to tick " + tick.TickID);
|
|
if (!(this is VMFSORDriver)) doTick = false; //something weird here. this can break loading from saves casually - but must not be active for resyncs.
|
|
//disable just for fsor playback
|
|
}
|
|
|
|
var caller = vm.GetAvatarByPersist(cmd.Command.ActorUID);
|
|
Executing = cmd;
|
|
cmd.Command.Execute(vm, caller);
|
|
Executing = null;
|
|
if (vm.FSOVAsyncLoading)
|
|
{
|
|
tick.Commands.Remove(cmd); //don't worry about modifying an enumerated collection - we are exiting anyways.
|
|
AsyncBreak = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (tick.TickID < LastTick) System.Console.WriteLine("Tick wrong! Got " + tick.TickID + ", Missed " + ((int)tick.TickID - (LastTick + 1)));
|
|
else if (doTick && vm.Context.Ready)
|
|
{
|
|
if (tick.TickID > LastTick + 1) System.Console.WriteLine("Tick wrong! Got " + tick.TickID + ", Missed " + ((int)tick.TickID - (LastTick + 1)));
|
|
vm.Trace?.NewTick(tick.TickID);
|
|
vm.InternalTick(tick.TickID);
|
|
if (DesyncCooldown > 0) DesyncCooldown--;
|
|
}
|
|
LastTick = tick.TickID;
|
|
}
|
|
|
|
//functions for recording command streams.
|
|
//Should work on client or server, assuming there is a sync point at the start.
|
|
|
|
public void Record(Stream output)
|
|
{
|
|
RecordStream = new BinaryWriter(output);
|
|
RecordStream.Write(new char[] { 'F', 'S', 'O', 'r' });
|
|
RecordStream.Write(0); //version, for future extension
|
|
}
|
|
|
|
public void RecordTick(VMNetTick tick)
|
|
{
|
|
tick.SerializeInto(RecordStream);
|
|
}
|
|
|
|
public void EndRecord()
|
|
{
|
|
RecordStream?.BaseStream?.Close();
|
|
}
|
|
|
|
public virtual void Shutdown()
|
|
{
|
|
if (OnShutdown != null) OnShutdown(CloseReason);
|
|
}
|
|
|
|
public delegate void VMNetMessageHandler(VMNetMessageType type, byte[] data);
|
|
}
|
|
|
|
public enum VMCloseNetReason
|
|
{
|
|
Unspecified = 0,
|
|
LeaveLot = 1,
|
|
ServerShutdown = 2,
|
|
NetException = 3, //server sent a bad tick
|
|
NetExceptionDirect = 4
|
|
}
|
|
}
|