using System.Collections; using UnityEngine; using UnityEngine.Networking; /// /// Clickable that boots a PCjs OS on the monitor (adds a MonitorBrowser to the screen) and glides the camera /// to the Computer close-up. Two of these sit on the desk so the player can choose: the Mouse boots Windows 95, /// the Keyboard boots Windows 3.1. Each names an OS so the shared monitor shows what's currently running and /// clicking the OTHER launcher while one is running saves it and swaps to the new one instead of doing nothing. /// /// Saved sessions: before booting, the launcher asks the server (afx-api) for this player's newest saved state /// of the machine and, if there is one, boots the page with ?state= so Windows carries on exactly /// where it was. The MonitorBrowser then keeps saving while it runs and once more when switched off. /// [RequireComponent(typeof(Collider))] public class MonitorBrowserLauncher : MonoBehaviour, ILobbyClickable { public Renderer screen; public ViewTarget computerView; public string osName = "Windows 95"; public string url = "http://78.110.173.123/VM/win95/blog/2015/09/21/"; public string offlineUrl = "http://78.110.173.123/VM/win95/blog/2015/09/21/"; [Tooltip("Prefix of the PCjs state key for this machine (the from machine.xml plus a dot), used to find the player's latest save.")] public string machineId = "deskpro386."; [Tooltip("Host running afx-api (and serving the /VM mirrors).")] public string apiBase = "http://78.110.173.123"; public float autoSaveEvery = 120f; [Tooltip("Texture tiling/offset for the browser picture on this screen (see MonitorBrowser.flipScale). (1,-1)/(0,1) = vertical flip, the confirmed setting for the monitor quad.")] public Vector2 screenFlipScale = new Vector2(1f, -1f), screenFlipOffset = new Vector2(0f, 1f); [Tooltip("How the saved state is used on boot. Disk = boot the machine normally onto the saved hard disks: files persist, " + "the running session does not, like a real PC (the default for both machines). Full = resume the exact snapshot " + "(works for Windows 3.1 only; PCjs cannot restore a protected-mode Windows 95 snapshot).")] public ResumeMode resumeMode = ResumeMode.Disk; public enum ResumeMode { Full, Disk } bool booting; public string Hint { get { var running = screen != null ? screen.GetComponent() : null; if (running == null) return osName + ": click to boot it on the PC (" + (resumeMode == ResumeMode.Disk ? "your files are kept between sessions" : "carries on from your last save") + ")"; return running.osName == osName ? osName + " is running (F1 saves and switches it off)" : "Save and switch to " + osName + " (currently running " + running.osName + ")"; } } public void Click() { if (screen == null || booting) return; var running = screen.GetComponent(); if (running != null) { if (running.osName == osName) { if (computerView != null) LobbyCamera.RequestView(computerView); return; } running.ShutDown(); // saves first; MonitorBrowser.OnDestroyed() then resumes the stream and we boot the new OS } if (computerView != null) LobbyCamera.RequestView(computerView); StartCoroutine(BootWhenFree()); } [System.Serializable] class ApiReply { public string code; public string data; } IEnumerator BootWhenFree() { booting = true; while (screen.GetComponent() != null) yield return null; // Our own v86 pages take the player id in the URL ({user}) and load their own saved disk changes; only the // PCjs pages need the saved state looked up here and passed as ?state=. if (url.Contains("{user}")) { string u = url.Replace("{user}", AfxPlayer.Id); var v = screen.gameObject.AddComponent(); v.screen = screen; v.osName = osName; v.userId = AfxPlayer.Id; v.autoSaveEvery = autoSaveEvery; v.flipScale = screenFlipScale; v.flipOffset = screenFlipOffset; v.SetUrl(u, string.IsNullOrEmpty(offlineUrl) ? "" : offlineUrl.Replace("{user}", AfxPlayer.Id)); booting = false; yield break; } // Newest saved state for this machine and player, if any (404 = fresh boot from the stock image/state) string stateQuery = ""; string q = apiBase + "/api/v1/user?req=latest&user=" + AfxPlayer.Id + "&machine=" + UnityWebRequest.EscapeURL(machineId) + (resumeMode == ResumeMode.Disk ? "&mode=disk" : ""); using (var req = UnityWebRequest.Get(q)) { req.timeout = 5; yield return req.SendWebRequest(); if (req.result == UnityWebRequest.Result.Success && req.responseCode == 200) { ApiReply reply = null; try { reply = JsonUtility.FromJson(req.downloadHandler.text); } catch { } if (reply != null && reply.code == "ok" && !string.IsNullOrEmpty(reply.data)) stateQuery = "?state=" + reply.data; } } Boot(stateQuery); booting = false; } void Boot(string stateQuery) { var b = screen.gameObject.AddComponent(); b.screen = screen; b.osName = osName; b.SetUrl(url + stateQuery, string.IsNullOrEmpty(offlineUrl) ? "" : offlineUrl + stateQuery); b.userId = AfxPlayer.Id; b.autoSaveEvery = autoSaveEvery; b.flipScale = screenFlipScale; b.flipOffset = screenFlipOffset; } }