using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using Munga.Net; namespace TeslaConsole; public class MungaGame { private readonly object mInternalLock = new object(); private readonly Pod mPod; private Image mImage; private string mStatusMessage; private MungaSocket mSocket; private DateTime mNextStateQuery = DateTime.MinValue; private ApplicationState? mAppState; private ApplicationID? mAppID; private DateTime mEggDelayTimer = DateTime.MinValue; private DateTime mEggSent = DateTime.MinValue; private bool mEggAck; private MungaConnectionState mState; private bool mDosBoxAddressShift; private readonly List mRequestors = new List(); private Form mOwner; public Image Image => mImage; public string StatusMessage => mStatusMessage; public Pod Pod => mPod; public ApplicationState? AppState => mAppState; public ApplicationID? AppID => mAppID; public DateTime EggDelayTimer { get { return mEggDelayTimer; } set { mEggDelayTimer = value; } } public DateTime EggSent { get { return mEggSent; } set { mEggSent = value; } } public bool EggAcknowledged => mEggAck; public MungaConnectionState State { get { lock (mInternalLock) { if (mState == MungaConnectionState.Connected) { if (mSocket != null && mSocket.Connected) { return MungaConnectionState.Connected; } Disconnect(); } return mState; } } private set { lock (mInternalLock) { mState = value; } } } public bool Requested { get { lock (mInternalLock) { return mRequestors.Count > 0; } } } /// /// When set, the game connection targets the pod's DOSBox-X guest /// (last octet +100) instead of the pod itself. Changing the value while /// connected drops the connection; it reconnects at the new address as /// long as the game is still requested. /// internal bool DosBoxAddressShift { get { lock (mInternalLock) { return mDosBoxAddressShift; } } set { lock (mInternalLock) { if (mDosBoxAddressShift != value) { mDosBoxAddressShift = value; if (mState != MungaConnectionState.Disconnected) { Disconnect(); } } } } } public bool IsOwned { get { if (mOwner != null && mOwner.IsDisposed) { mOwner = null; } return mOwner != null; } } public event EventHandler OnPodStatusUpdated; public void QueryStateIfNeeded() { DateTime now = DateTime.Now; if (mNextStateQuery < now) { Send(0, 1, new StateQueryMessage(1)); mNextStateQuery = now.AddSeconds(1.0); } } public bool IsPodAppState(ApplicationID app, ApplicationState state) { return IsPodAppState(app, new ApplicationState[1] { state }); } public bool IsPodAppState(ApplicationID app, IEnumerable states) { if (IsPodOperational(app)) { foreach (ApplicationState state in states) { if (mAppState.Value == state) { return true; } } } return false; } public bool IsPodOperational(ApplicationID app) { if (State == MungaConnectionState.Connected && mAppID.HasValue && mAppID.Value == app) { return mAppState.HasValue; } return false; } public void MakeRequested(object requestor) { lock (mInternalLock) { if (!mRequestors.Contains(requestor)) { mRequestors.Add(requestor); if (mRequestors.Count == 1) { Connect(); } } } } public void ReleaseRequest(object requestor) { lock (mInternalLock) { if (mRequestors.Contains(requestor)) { mRequestors.Remove(requestor); if (mRequestors.Count == 0) { Disconnect(); } } } } public bool IsOwner(Form owner) { if (mOwner != null && mOwner.IsDisposed) { mOwner = null; } return mOwner == owner; } public void TakeOwnership(Form owner) { lock (mInternalLock) { if (mOwner == null) { mOwner = owner; ForceStatusUpdate(); } } } public void ReleaseOwnership(Form owner) { lock (mInternalLock) { if (mOwner == owner) { mOwner = null; ForceStatusUpdate(); } } } public MungaGame(Pod pod) { mPod = pod; } public void SetStatus(Image img, string statusMessage) { lock (mInternalLock) { if (mImage != img || mStatusMessage != statusMessage) { mImage = img; mStatusMessage = statusMessage; ForceStatusUpdate(); } } } private void ForceStatusUpdate() { if (this.OnPodStatusUpdated != null) { this.OnPodStatusUpdated(this, EventArgs.Empty); } } private void Connect() { lock (mInternalLock) { if (State != 0) { return; } State = MungaConnectionState.Connecting; ShutdownSocket(); mSocket = new MungaSocket(); mSocket.BeginConnect(mDosBoxAddressShift ? DosBox.ShiftAddress(mPod.IPAddress) : mPod.IPAddress, 1501, delegate(IAsyncResult ar) { lock (mInternalLock) { try { mSocket.EndConnect(ar); State = MungaConnectionState.Connected; } catch { Disconnect(); } } }, null); } } private void Disconnect() { lock (mInternalLock) { State = MungaConnectionState.Disconnected; mAppState = null; mAppID = null; mNextStateQuery = DateTime.MinValue; ShutdownSocket(); if (Requested) { Connect(); } } } private void ShutdownSocket() { if (mSocket != null) { try { mSocket.Shutdown(); } catch { } mSocket = null; } } public MetaMessage Receive() { lock (mInternalLock) { try { MetaMessage metaMessage = mSocket.Receive(); if (metaMessage != null) { if (metaMessage.Message is StateResponseMessage) { StateResponseMessage stateResponseMessage = (StateResponseMessage)metaMessage.Message; mAppID = stateResponseMessage.Application; if (stateResponseMessage.ApplicationState == ApplicationState.WaitingForEgg && stateResponseMessage.ApplicationState != mAppState) { mEggDelayTimer = DateTime.Now.AddSeconds(5.0); mEggSent = DateTime.MinValue; mEggAck = false; } mAppState = stateResponseMessage.ApplicationState; } else if (metaMessage.Message is AcknowledgeEggFileMessage) { mEggAck = true; } } return metaMessage; } catch { Disconnect(); return null; } } } public void Send(int gameID, int fromHost, MungaMessage message) { lock (mInternalLock) { try { mSocket.Send(gameID, fromHost, message); } catch { Disconnect(); } } } }