ShiftOS_TheReturn/ShiftOS.Frontend/ShiftOS.cs

402 lines
15 KiB
C#
Raw Normal View History

2017-07-02 18:09:07 +00:00
using System;
2017-07-05 12:43:35 +00:00
using System.Collections.Generic;
using System.Linq;
2017-07-02 18:09:07 +00:00
using System.Runtime.InteropServices;
using Microsoft.Xna.Framework;
2017-07-02 17:31:39 +00:00
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
2017-07-05 12:43:35 +00:00
using Newtonsoft.Json;
using ShiftOS.Engine;
2017-07-02 17:31:39 +00:00
using ShiftOS.Frontend.GraphicsSubsystem;
namespace ShiftOS.Frontend
{
/// <summary>
/// This is the main type for your game.
/// </summary>
public class ShiftOS : Game
{
GraphicsDeviceManager GraphicsDevice;
SpriteBatch spriteBatch;
2017-07-05 14:34:09 +00:00
private bool DisplayDebugInfo = false;
2017-07-02 17:31:39 +00:00
public ShiftOS()
{
GraphicsDevice = new GraphicsDeviceManager(this);
2017-07-26 03:25:52 +00:00
var uconf = Objects.UserConfig.Get();
GraphicsDevice.PreferredBackBufferHeight = uconf.ScreenHeight;
GraphicsDevice.PreferredBackBufferWidth = uconf.ScreenWidth;
2017-07-05 12:43:35 +00:00
SkinEngine.SkinLoaded += () =>
{
UIManager.ResetSkinTextures(GraphicsDevice.GraphicsDevice);
UIManager.InvalidateAll();
};
2017-07-04 13:52:49 +00:00
UIManager.Viewport = new System.Drawing.Size(
GraphicsDevice.PreferredBackBufferWidth,
GraphicsDevice.PreferredBackBufferHeight
);
2017-07-02 17:31:39 +00:00
Content.RootDirectory = "Content";
2017-07-02 17:31:39 +00:00
//Make window borderless
Window.IsBorderless = false;
2017-07-02 17:31:39 +00:00
//Set the title
Window.Title = "ShiftOS";
2017-07-02 17:31:39 +00:00
//Fullscreen
GraphicsDevice.IsFullScreen = false;
2017-07-02 17:31:39 +00:00
}
private Keys lastKey = Keys.None;
2017-07-02 17:31:39 +00:00
/// <summary>
/// Allows the game to perform any initialization it needs to before starting to run.
/// This is where it can query for any required services and load any non-graphic
/// related content. Calling base.Initialize will enumerate through any components
/// and initialize them as well.
/// </summary>
protected override void Initialize()
{
OutOfBoxExperience.Init(new MonoGameOOBE());
//Before we do ANYTHING, we've got to initiate the ShiftOS engine.
2017-07-05 17:15:00 +00:00
UIManager.GraphicsDevice = GraphicsDevice.GraphicsDevice;
//Let's get localization going.
Localization.RegisterProvider(new MonoGameLanguageProvider());
//First things first, let's initiate the window manager.
AppearanceManager.Initiate(new Desktop.WindowManager());
//Cool. Now the engine's window management system talks to us.
//Also initiate the desktop
Engine.Desktop.Init(new Desktop.Desktop());
2017-07-14 01:30:04 +00:00
//While we're having a damn initiation fuckfest, let's get the hacking engine running.
Hacking.Initiate();
//Now we can initiate the Infobox subsystem
Engine.Infobox.Init(new Infobox());
2017-07-05 12:43:35 +00:00
2017-07-03 15:29:54 +00:00
//Let's initiate the engine just for a ha.
TerminalBackend.TerminalRequested += () =>
{
AppearanceManager.SetupWindow(new Apps.Terminal());
};
FileSkimmerBackend.Init(new MGFSLayer());
2017-07-25 21:57:50 +00:00
//Create a main menu
var mm = new MainMenu();
UIManager.AddTopLevel(mm);
2017-07-03 15:29:54 +00:00
2017-07-02 17:31:39 +00:00
base.Initialize();
}
2017-07-06 22:25:19 +00:00
private double timeSinceLastPurge = 0;
private Texture2D MouseTexture = null;
/// <summary>
2017-07-02 17:31:39 +00:00
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
this.spriteBatch = new SpriteBatch(base.GraphicsDevice);
2017-07-05 12:43:35 +00:00
UIManager.ResetSkinTextures(GraphicsDevice.GraphicsDevice);
2017-07-02 17:31:39 +00:00
// TODO: use this.Content to load your game content here
var bmp = Properties.Resources.cursor_9x_pointer;
var _lock = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
byte[] rgb = new byte[Math.Abs(_lock.Stride) * _lock.Height];
Marshal.Copy(_lock.Scan0, rgb, 0, rgb.Length);
bmp.UnlockBits(_lock);
MouseTexture = new Texture2D(GraphicsDevice.GraphicsDevice, bmp.Width, bmp.Height);
MouseTexture.SetData<byte>(rgb);
rgb = null;
2017-07-02 17:31:39 +00:00
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// game-specific content.
/// </summary>
protected override void UnloadContent()
{
MouseTexture = null;
2017-07-02 17:31:39 +00:00
// TODO: Unload any non ContentManager content here
}
2017-07-03 20:17:43 +00:00
private double kb_elapsedms = 0;
private double mouseMS = 0;
2017-07-03 20:17:43 +00:00
private MouseState LastMouseState;
2017-07-02 17:31:39 +00:00
/// <summary>
/// Allows the game to run logic such as updating the world,
/// checking for collisions, gathering input, and playing audio.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Update(GameTime gameTime)
{
if (UIManager.CrossThreadOperations.Count > 0)
{
var action = UIManager.CrossThreadOperations.Dequeue();
action?.Invoke();
}
2017-07-02 17:31:39 +00:00
//Let's get the mouse state
var mouseState = Mouse.GetState(this.Window);
LastMouseState = mouseState;
2017-07-05 12:43:35 +00:00
UIManager.ProcessMouseState(LastMouseState, mouseMS);
if (mouseState.LeftButton == ButtonState.Pressed)
{
mouseMS = 0;
}
else
{
mouseMS += gameTime.ElapsedGameTime.TotalMilliseconds;
}
//So we have mouse input, and the UI layout system working...
//But an OS isn't useful without the keyboard!
//Let's see how keyboard input works.
//Hmmm... just like the mouse...
var keystate = Keyboard.GetState();
//Simple... just iterate through this list and generate some key events?
var keys = keystate.GetPressedKeys();
if (keys.Length > 0)
{
var key = keys.FirstOrDefault(x => x != Keys.LeftControl && x != Keys.RightControl && x != Keys.LeftShift && x != Keys.RightShift && x != Keys.LeftAlt && x != Keys.RightAlt);
2017-07-03 20:17:43 +00:00
if(lastKey != key)
{
2017-07-03 20:17:43 +00:00
kb_elapsedms = 0;
lastKey = key;
}
}
2017-07-03 20:17:43 +00:00
if (keystate.IsKeyDown(lastKey))
{
if (kb_elapsedms == 0 || kb_elapsedms >= 500)
{
if (lastKey == Keys.F11)
{
GraphicsDevice.IsFullScreen = !GraphicsDevice.IsFullScreen;
GraphicsDevice.ApplyChanges();
}
else
{
var shift = keystate.IsKeyDown(Keys.LeftShift) || keystate.IsKeyDown(Keys.RightShift);
var alt = keystate.IsKeyDown(Keys.LeftAlt) || keystate.IsKeyDown(Keys.RightAlt);
var control = keystate.IsKeyDown(Keys.LeftControl) || keystate.IsKeyDown(Keys.RightControl);
2017-07-05 14:34:09 +00:00
if (control && lastKey == Keys.D)
{
DisplayDebugInfo = !DisplayDebugInfo;
}
2017-07-05 17:15:00 +00:00
else if(control && lastKey == Keys.E)
{
UIManager.ExperimentalEffects = !UIManager.ExperimentalEffects;
}
2017-07-05 14:34:09 +00:00
else
{
var e = new KeyEvent(control, alt, shift, lastKey);
UIManager.ProcessKeyEvent(e);
}
}
}
2017-07-03 20:17:43 +00:00
kb_elapsedms += gameTime.ElapsedGameTime.TotalMilliseconds;
}
else
{
kb_elapsedms = 0;
}
//Cause layout update on all elements
UIManager.LayoutUpdate(gameTime);
2017-07-06 22:25:19 +00:00
timeSinceLastPurge += gameTime.ElapsedGameTime.TotalSeconds;
if(timeSinceLastPurge > 30)
{
GraphicsContext.StringCaches.Clear();
timeSinceLastPurge = 0;
}
//Some hackables have a connection timeout applied to them.
//We must update timeout values here, and disconnect if the timeout
//hits zero.
if(Hacking.CurrentHackable != null)
{
if (Hacking.CurrentHackable.DoConnectionTimeout)
{
Hacking.CurrentHackable.MillisecondsCountdown -= gameTime.ElapsedGameTime.TotalMilliseconds;
if(Hacking.CurrentHackable.MillisecondsCountdown <= 0)
{
Hacking.FailHack();
}
}
}
2017-07-02 17:31:39 +00:00
base.Update(gameTime);
}
2017-07-02 18:09:07 +00:00
private GUI.TextControl framerate = new GUI.TextControl();
2017-07-02 17:31:39 +00:00
/// <summary>
/// This is called when the game should draw itself.
/// </summary>
/// <param name="gameTime">Provides a snapshot of timing values.</param>
protected override void Draw(GameTime gameTime)
{
UIManager.DrawControlsToTargets(GraphicsDevice.GraphicsDevice, spriteBatch, 0, 0);
2017-07-06 02:24:17 +00:00
var rasterizerState = new RasterizerState();
rasterizerState.CullMode = CullMode.None;
rasterizerState.MultiSampleAntiAlias = true;
2017-07-05 14:34:09 +00:00
spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied,
SamplerState.LinearClamp, DepthStencilState.Default,
2017-07-06 02:24:17 +00:00
rasterizerState);
2017-07-02 17:31:39 +00:00
//Draw the desktop BG.
UIManager.DrawBackgroundLayer(GraphicsDevice.GraphicsDevice, spriteBatch, 640, 480);
2017-07-02 17:31:39 +00:00
//The desktop is drawn, now we can draw the UI.
UIManager.DrawTArgets(spriteBatch);
2017-07-02 17:31:39 +00:00
2017-07-02 18:09:07 +00:00
//Draw a mouse cursor
2017-07-02 18:09:07 +00:00
var mousepos = Mouse.GetState(this.Window).Position;
2017-07-05 17:15:00 +00:00
spriteBatch.Draw(MouseTexture, new Rectangle(mousepos.X+1, mousepos.Y+1, MouseTexture.Width, MouseTexture.Height), Color.Black * 0.5f);
spriteBatch.Draw(MouseTexture, new Rectangle(mousepos.X, mousepos.Y, MouseTexture.Width, MouseTexture.Height), Color.White);
2017-07-05 17:15:00 +00:00
if(Hacking.CurrentHackable != null)
{
if (Hacking.CurrentHackable.DoConnectionTimeout)
{
string str = $"Connection TImeout in {(Hacking.CurrentHackable.MillisecondsCountdown / 1000).ToString("#.##")} seconds.";
var gfx = new GraphicsContext(GraphicsDevice.GraphicsDevice, spriteBatch, 0, 0, UIManager.Viewport.Width, UIManager.Viewport.Height);
var measure = gfx.MeasureString(str, SkinEngine.LoadedSkin.HeaderFont);
gfx.DrawString(str, 5, (gfx.Height - ((int)measure.Y) - 5), Color.Red, SkinEngine.LoadedSkin.HeaderFont);
}
}
2017-07-05 14:34:09 +00:00
if (DisplayDebugInfo)
{
var gfxContext = new GraphicsContext(GraphicsDevice.GraphicsDevice, spriteBatch, 0, 0, GraphicsDevice.PreferredBackBufferWidth, GraphicsDevice.PreferredBackBufferHeight);
2017-07-06 02:24:17 +00:00
var color = Color.White;
2017-07-06 22:25:19 +00:00
double fps = Math.Round(1 / gameTime.ElapsedGameTime.TotalSeconds);
2017-07-06 02:24:17 +00:00
if (fps <= 20)
color = Color.Red;
2017-07-05 17:15:00 +00:00
gfxContext.DrawString($@"ShiftOS 1.0 Beta 4
Copyright (c) 2017 Michael VanOverbeek, Rylan Arbour, RogueAI
This is an unstable build.
2017-07-06 02:24:17 +00:00
FPS: {(fps)}
An FPS below 20 can cause glitches in keyboard and mouse handling. It is advised that if you are getting these
framerates, press CTRL+E to disable fancy effects, close any apps you are not using, and try running in windowed mode
or in a lower resolution.
If all else fails, you can set a breakpoint somewhere in the ShiftOS.Update() or ShiftOS.Draw() methods in the game's source
code and use Visual Studio's debugger to step through the code to find bottlenecks.
If a method takes more than 30 milliseconds to complete, that is a sign that it is bottlenecking the game and may need to be
optimized.
Try using the SkinTextures cache when rendering skin elements, and try using the GraphicsContext.DrawString() method when drawing
text. In this build, we are aware that this method causes bottlenecking, we are working on a caching system for fonts so we do not need to
use the System.Drawing.Graphics class to draw text.
2017-07-05 17:15:00 +00:00
UI render target count: {UIManager.TextureCaches.Count}
Skin texture caches: {UIManager.SkinTextures.Count}
Open windows (excluding dialog boxes): {AppearanceManager.OpenForms.Count}
Experimental effects enabled: {UIManager.ExperimentalEffects}
Fullscreen: {GraphicsDevice.IsFullScreen}
Game resolution: {GraphicsDevice.PreferredBackBufferWidth}x{GraphicsDevice.PreferredBackBufferHeight}
Mouse state:
X: {LastMouseState.X}
Y: {LastMouseState.Y}
Last left click MS: {mouseMS}
", 0, 0, color, new System.Drawing.Font("Lucida Console", 9F, System.Drawing.FontStyle.Bold));
2017-07-05 14:34:09 +00:00
}
2017-07-02 18:09:07 +00:00
2017-07-02 17:31:39 +00:00
spriteBatch.End();
base.Draw(gameTime);
}
}
2017-07-05 12:43:35 +00:00
[ShiftoriumProvider]
public class MonoGameShiftoriumProvider : IShiftoriumProvider
{
public List<ShiftoriumUpgrade> GetDefaults()
{
return JsonConvert.DeserializeObject<List<ShiftoriumUpgrade>>(Properties.Resources.Shiftorium);
}
}
public class MGFSLayer : IFileSkimmer
{
public string GetFileExtension(FileType fileType)
{
switch (fileType)
{
case FileType.CommandFormat:
return ".cf";
case FileType.Executable:
return ".saa";
case FileType.Filesystem:
return ".mfs";
case FileType.Image:
return ".png";
case FileType.JSON:
return ".json";
case FileType.Lua:
return ".lua";
case FileType.Python:
return ".py";
case FileType.Skin:
return ".skn";
case FileType.TextFile:
return ".txt";
default:
return ".scrtm";
}
}
public void GetPath(string[] filetypes, FileOpenerStyle style, Action<string> callback)
{
throw new NotImplementedException();
}
public void OpenDirectory(string path)
{
throw new NotImplementedException();
}
}
2017-07-02 17:31:39 +00:00
}