More work on new launcher.

This commit is contained in:
UnknownShadow200 2015-10-17 20:20:44 +11:00
parent 92d32f6b3b
commit 87be12ec71
17 changed files with 1029 additions and 210 deletions

View File

@ -91,6 +91,13 @@ namespace ClassicalSharp {
g.Clear( colour ); g.Clear( colour );
} }
public override void Clear( Color colour, int x, int y, int width, int height ) {
g.SmoothingMode = SmoothingMode.None;
Brush brush = GetOrCreateBrush( colour );
g.FillRectangle( brush, x, y, width, height );
g.SmoothingMode = SmoothingMode.HighQuality;
}
public override void Dispose() { public override void Dispose() {
g.Dispose(); g.Dispose();
g = null; g = null;

View File

@ -35,6 +35,9 @@ namespace ClassicalSharp {
/// <summary> Clears the entire bound bitmap to the specified colour. </summary> /// <summary> Clears the entire bound bitmap to the specified colour. </summary>
public abstract void Clear( Color colour ); public abstract void Clear( Color colour );
/// <summary> Clears the entire bound bitmap to the specified colour. </summary>
public abstract void Clear( Color colour, int x, int y, int width, int height );
/// <summary> Disposes of any resources used by this class that are associated with the underlying bitmap. </summary> /// <summary> Disposes of any resources used by this class that are associated with the underlying bitmap. </summary>
public abstract void Dispose(); public abstract void Dispose();

View File

@ -11,9 +11,8 @@ namespace ClassicalSharp {
[STAThread] [STAThread]
public static void Main( string[] args ) { public static void Main( string[] args ) {
if( !Debugger.IsAttached ) { if( !Debugger.IsAttached )
AppDomain.CurrentDomain.UnhandledException += UnhandledException; AppDomain.CurrentDomain.UnhandledException += UnhandledException;
}
Utils.Log( "Starting " + Utils.AppName + ".." ); Utils.Log( "Starting " + Utils.AppName + ".." );
if( !File.Exists( "default.zip" ) ) { if( !File.Exists( "default.zip" ) ) {

52
Launcher2/Client.cs Normal file
View File

@ -0,0 +1,52 @@
using System;
using System.Diagnostics;
using System.IO;
using ClassicalSharp;
namespace Launcher2 {
public static class Client {
public static bool Start( GameStartData data, bool classicubeSkins ) {
string skinServer = classicubeSkins ? "http://www.classicube.net/static/skins/" : "http://s3.amazonaws.com/MinecraftSkins/";
string args = data.Username + " " + data.Mppass + " " +
data.Ip + " " + data.Port + " " + skinServer;
UpdateResumeInfo( data, classicubeSkins );
return Start( args );
}
public static bool Start( string args ) {
Process process = null;
if( !File.Exists( "ClassicalSharp.exe" ) )
return false;
if( Type.GetType( "Mono.Runtime" ) != null ) {
process = Process.Start( "mono", "\"ClassicalSharp.exe\" " + args );
} else {
process = Process.Start( "ClassicalSharp.exe", args );
}
return true;
}
internal static void UpdateResumeInfo( GameStartData data, bool classiCubeSkins ) {
// If the client has changed some settings in the meantime, make sure we keep the changes
try {
Options.Load();
} catch( IOException ) {
}
Options.Set( "launcher-username", data.Username );
Options.Set( "launcher-ip", data.Ip );
Options.Set( "launcher-port", data.Port );
Options.Set( "launcher-mppass", Secure.Encode( data.Mppass, data.Username ) );
Options.Set( "launcher-ccskins", classiCubeSkins );
try {
Options.Save();
} catch( IOException ) {
}
}
}
}

View File

@ -0,0 +1,252 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using ClassicalSharp;
using OpenTK;
using OpenTK.Input;
namespace Launcher2 {
public sealed class ClassiCubeScreen : LauncherScreen {
public ClassiCubeScreen( LauncherWindow game ) : base( game ) {
textFont = new Font( "Arial", 16, FontStyle.Bold );
widgets = new LauncherWidget[7];
game.Window.Mouse.Move += MouseMove;
game.Window.Mouse.ButtonDown += MouseButtonDown;
game.Window.KeyPress += KeyPress;
game.Window.Keyboard.KeyDown += KeyDown;
game.Window.Keyboard.KeyRepeat = true;
}
void KeyDown( object sender, KeyboardKeyEventArgs e ) {
if( lastInput != null && e.Key == Key.BackSpace ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
lastInput.RemoveChar( textFont );
Dirty = true;
}
} else if( e.Key == Key.Enter ) { // Click sign in button
LauncherWidget widget = widgets[4];
if( widget.OnClick != null )
widget.OnClick();
}
}
void KeyPress( object sender, KeyPressEventArgs e ) {
if( lastInput != null ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
lastInput.AddChar( e.KeyChar, textFont );
Dirty = true;
}
}
}
public override void Init() {
Resize();
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
LoadSavedInfo( drawer );
Dirty = true;
}
}
void LoadSavedInfo( IDrawer2D drawer ) {
try {
Options.Load();
} catch( IOException ) {
return;
}
string user = Options.Get( "launcher-cc-username" ) ?? "";
string pass = Options.Get( "launcher-cc-password" ) ?? "";
pass = Secure.Decode( pass, user );
Set( 2, user );
Set( 3, pass );
}
public override void Resize() {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
DrawButtons( drawer );
}
Dirty = true;
}
Font textFont;
static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 );
void DrawButtons( IDrawer2D drawer ) {
widgetIndex = 0;
MakeTextAt( drawer, "Username", -180, -100 );
MakeTextAt( drawer, "Password", -180, -50 );
MakeTextInputAt( drawer, false, Get( widgetIndex ), 30, -100 );
MakeTextInputAt( drawer, true, Get( widgetIndex ), 30, -50 );
MakeButtonAt( drawer, "Sign in", 110, 35, -65, 0, StartClient );
MakeButtonAt( drawer, "Back", 80, 35, 140, 0, () => game.SetScreen( new MainScreen( game ) ) );
MakeTextAt( drawer, "", 0, 50 );
}
ClassicubeSession session = new ClassicubeSession();
List<ServerListEntry> servers = new List<ServerListEntry>();
void StartClient() {
if( String.IsNullOrEmpty( Get( 2 ) ) ) {
SetStatus( "&ePlease enter a username" ); return;
}
if( String.IsNullOrEmpty( Get( 3 ) ) ) {
SetStatus( "&ePlease enter a username" ); return;
}
System.Diagnostics.Debug.WriteLine( Get( 2 ) );
System.Diagnostics.Debug.WriteLine( Get( 3 ) );
SetStatus( "&eSigning in..." );
try {
session.Login( Get( 2 ), Get( 3 ) );
} catch( WebException ex ) {
session.Username = null;
DisplayWebException( ex, "sign in" );
return;
} catch( InvalidOperationException ex ) {
session.Username = null;
string text = "&eFailed to sign in: " +
Environment.NewLine + ex.Message;
SetStatus( text );
return;
}
SetStatus( "&eRetrieving public servers list.." );
try {
servers = session.GetPublicServers();
} catch( WebException ex ) {
servers = new List<ServerListEntry>();
DisplayWebException( ex, "retrieve servers list" );
return;
}
SetStatus( "&eSigned in" );
}
string Get( int index ) {
LauncherWidget widget = widgets[index];
return widget == null ? "" : ((widget as LauncherTextInputWidget)).Text;
}
void Set( int index, string text ) {
(widgets[index] as LauncherTextInputWidget)
.Redraw( game.Drawer, text, textFont );
}
void SetStatus( string text ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
LauncherTextWidget widget = (LauncherTextWidget)widgets[6];
drawer.Clear( LauncherWindow.clearColour, widget.X, widget.Y,
widget.Width, widget.Height );
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 0, 50 );
Dirty = true;
}
}
void MakeTextAt( IDrawer2D drawer, string text, int x, int y ) {
LauncherTextWidget widget = new LauncherTextWidget( game );
widget.Text = text;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, x, y );
widgets[widgetIndex++] = widget;
}
void MakeTextInputAt( IDrawer2D drawer, bool password, string text, int x, int y ) {
LauncherTextInputWidget widget = new LauncherTextInputWidget( game );
widget.OnClick = InputClick;
widget.Password = password;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 300, 30, x, y );
widgets[widgetIndex++] = widget;
}
void MakeButtonAt( IDrawer2D drawer, string text, int width, int height,
int x, int y, Action onClick ) {
LauncherButtonWidget widget = new LauncherButtonWidget( game );
widget.Text = text;
widget.OnClick = onClick;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, width, height, x, y );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
widgets[widgetIndex++] = widget;
}
protected override void UnselectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
if( button != null ) {
button.Redraw( game.Drawer, button.Text, textFont );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
Dirty = true;
}
}
protected override void SelectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
if( button != null ) {
button.Redraw( game.Drawer, button.Text, textFont );
Dirty = true;
}
}
LauncherTextInputWidget lastInput;
void InputClick() {
LauncherTextInputWidget input = selectedWidget as LauncherTextInputWidget;
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
if( lastInput != null ) {
lastInput.Active = false;
lastInput.Redraw( game.Drawer, lastInput.Text, textFont );
}
input.Active = true;
input.Redraw( game.Drawer, input.Text, textFont );
}
lastInput = input;
Dirty = true;
}
public override void Dispose() {
textFont.Dispose();
game.Window.Mouse.Move -= MouseMove;
game.Window.Mouse.ButtonDown -= MouseButtonDown;
game.Window.KeyPress -= KeyPress;
game.Window.Keyboard.KeyDown -= KeyDown;
game.Window.Keyboard.KeyRepeat = false;
}
void DisplayWebException( WebException ex, string action ) {
Program.LogException( ex );
if( ex.Status == WebExceptionStatus.Timeout ) {
string text = "&eFailed to " + action + ":" +
Environment.NewLine + "Timed out while connecting to classicube.net.";
SetStatus( text );
} else if( ex.Status == WebExceptionStatus.ProtocolError ) {
HttpWebResponse response = (HttpWebResponse)ex.Response;
int errorCode = (int)response.StatusCode;
string description = response.StatusDescription;
string text = "&eFailed to " + action + ":" +
Environment.NewLine + " classicube.net returned: (" + errorCode + ") " + description;
SetStatus(text );
} else if( ex.Status == WebExceptionStatus.NameResolutionFailure ) {
string text = "&eFailed to " + action + ":" +
Environment.NewLine + "Unable to resolve classicube.net" +
Environment.NewLine + "you may not be connected to the internet.";
SetStatus( text );
} else {
string text = "&eFailed to " + action + ":" +
Environment.NewLine + ex.Status;
SetStatus( text );
}
}
}
}

View File

@ -0,0 +1,226 @@
using System;
using System.Drawing;
using System.IO;
using System.Net;
using ClassicalSharp;
using OpenTK;
using OpenTK.Input;
namespace Launcher2 {
public sealed class DirectConnectScreen : LauncherScreen {
public DirectConnectScreen( LauncherWindow game ) : base( game ) {
textFont = new Font( "Arial", 16, FontStyle.Bold );
widgets = new LauncherWidget[9];
game.Window.Mouse.Move += MouseMove;
game.Window.Mouse.ButtonDown += MouseButtonDown;
game.Window.KeyPress += KeyPress;
game.Window.Keyboard.KeyDown += KeyDown;
game.Window.Keyboard.KeyRepeat = true;
}
void KeyDown( object sender, KeyboardKeyEventArgs e ) {
if( lastInput != null && e.Key == Key.BackSpace ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
lastInput.RemoveChar( textFont );
Dirty = true;
}
} else if( e.Key == Key.Enter ) { // Click connect button
LauncherWidget widget = widgets[6];
if( widget.OnClick != null )
widget.OnClick();
}
}
void KeyPress( object sender, KeyPressEventArgs e ) {
if( lastInput != null ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
lastInput.AddChar( e.KeyChar, textFont );
Dirty = true;
}
}
}
public override void Init() {
Resize();
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
LoadSavedInfo( drawer );
Dirty = true;
}
}
void LoadSavedInfo( IDrawer2D drawer ) {
try {
Options.Load();
} catch( IOException ) {
return;
}
string user = Options.Get( "launcher-username" ) ?? "";
string ip = Options.Get( "launcher-ip" ) ?? "127.0.0.1";
string port = Options.Get( "launcher-port" ) ?? "25565";
IPAddress address;
if( !IPAddress.TryParse( ip, out address ) ) ip = "127.0.0.1";
ushort portNum;
if( !UInt16.TryParse( port, out portNum ) ) port = "25565";
string mppass = Options.Get( "launcher-mppass" ) ?? null;
mppass = Secure.Decode( mppass, user );
Set( 3, user );
Set( 4, ip + ":" + port );
Set( 5, mppass );
}
public override void Resize() {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
DrawButtons( drawer );
}
Dirty = true;
}
Font textFont;
static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 );
void DrawButtons( IDrawer2D drawer ) {
widgetIndex = 0;
MakeTextAt( drawer, "Username", -180, -100 );
MakeTextAt( drawer, "Address", -180, -50 );
MakeTextAt( drawer, "Mppass", -180, 0 );
MakeTextInputAt( drawer, Get( widgetIndex ), 30, -100 );
MakeTextInputAt( drawer, Get( widgetIndex ), 30, -50 );
MakeTextInputAt( drawer, Get( widgetIndex ), 30, 0 );
MakeButtonAt( drawer, "Connect", 110, 35, -65, 50, StartClient );
MakeButtonAt( drawer, "Back", 80, 35, 140, 50, () => game.SetScreen( new MainScreen( game ) ) );
MakeTextAt( drawer, "", 0, 100 );
}
void StartClient() {
SetStatus( "" );
if( String.IsNullOrEmpty( Get( 3 ) ) ) {
SetStatus( "&ePlease enter a username" ); return;
}
string address = Get( 4 );
int index = address.LastIndexOf( ':' );
if( index <= 0 || index == address.Length - 1 ) {
SetStatus( "&eInvalid address" ); return;
}
string ipPart = address.Substring( 0, index ); IPAddress ip;
if( !IPAddress.TryParse( ipPart, out ip ) ) {
SetStatus( "&eInvalid ip" ); return;
}
string portPart = address.Substring( index + 1, address.Length - index - 1 ); ushort port;
if( !UInt16.TryParse( portPart, out port ) ) {
SetStatus( "&eInvalid port" ); return;
}
string mppass = Get( 5 );
if( String.IsNullOrEmpty( mppass ) ) mppass = "(none)";
GameStartData data = new GameStartData( Get( 3 ), mppass, ipPart, portPart );
Client.Start( data, false );
}
string Get( int index ) {
LauncherWidget widget = widgets[index];
return widget == null ? "" : ((widget as LauncherTextInputWidget)).Text;
}
void Set( int index, string text ) {
(widgets[index] as LauncherTextInputWidget)
.Redraw( game.Drawer, text, textFont );
}
void SetStatus( string text ) {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
LauncherTextWidget widget = (LauncherTextWidget)widgets[8];
drawer.Clear( LauncherWindow.clearColour, widget.X, widget.Y,
widget.Width, widget.Height );
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 0, 100 );
Dirty = true;
}
}
void MakeTextAt( IDrawer2D drawer, string text, int x, int y ) {
LauncherTextWidget widget = new LauncherTextWidget( game );
widget.Text = text;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, x, y );
widgets[widgetIndex++] = widget;
}
void MakeTextInputAt( IDrawer2D drawer, string text, int x, int y ) {
LauncherTextInputWidget widget = new LauncherTextInputWidget( game );
widget.OnClick = InputClick;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, 300, 30, x, y );
widgets[widgetIndex++] = widget;
}
void MakeButtonAt( IDrawer2D drawer, string text, int width, int height,
int x, int y, Action onClick ) {
LauncherButtonWidget widget = new LauncherButtonWidget( game );
widget.Text = text;
widget.OnClick = onClick;
widget.DrawAt( drawer, text, textFont, Anchor.Centre, Anchor.Centre, width, height, x, y );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
widgets[widgetIndex++] = widget;
}
protected override void UnselectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
if( button != null ) {
button.Redraw( game.Drawer, button.Text, textFont );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
Dirty = true;
}
}
protected override void SelectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
if( button != null ) {
button.Redraw( game.Drawer, button.Text, textFont );
Dirty = true;
}
}
LauncherTextInputWidget lastInput;
void InputClick() {
LauncherTextInputWidget input = selectedWidget as LauncherTextInputWidget;
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
if( lastInput != null ) {
lastInput.Active = false;
lastInput.Redraw( game.Drawer, lastInput.Text, textFont );
}
input.Active = true;
input.Redraw( game.Drawer, input.Text, textFont );
}
lastInput = input;
Dirty = true;
}
public override void Dispose() {
textFont.Dispose();
game.Window.Mouse.Move -= MouseMove;
game.Window.Mouse.ButtonDown -= MouseButtonDown;
game.Window.KeyPress -= KeyPress;
game.Window.Keyboard.KeyDown -= KeyDown;
game.Window.Keyboard.KeyRepeat = false;
}
}
}

View File

@ -0,0 +1,87 @@
using System;
using ClassicalSharp;
using OpenTK.Input;
namespace Launcher2 {
public abstract class LauncherScreen {
protected LauncherWindow game;
public bool Dirty;
protected int widgetIndex;
public LauncherScreen( LauncherWindow game ) {
this.game = game;
}
public abstract void Init();
public abstract void Resize();
public abstract void Dispose();
static uint clearColourBGRA = (uint)LauncherWindow.clearColour.ToArgb();
protected unsafe void FilterArea( int x, int y, int width, int height, byte scale ) {
using( FastBitmap bmp = new FastBitmap( game.Framebuffer, true ) ) {
for( int yy = y; yy < y + height; yy++ ) {
int* row = bmp.GetRowPtr( yy ) + x;
for( int xx = 0; xx < width; xx++ ) {
uint pixel = (uint)row[xx];
if( pixel == clearColourBGRA ) continue;
uint a = pixel & 0xFF000000;
uint r = (pixel >> 16) & 0xFF;
uint g = (pixel >> 8) & 0xFF;
uint b = pixel & 0xFF;
r = (r * scale) / 255;
g = (g * scale) / 255;
b = (b * scale) / 255;
row[xx] = (int)(a | (r << 16) | (g << 8) | b);
}
}
}
}
protected LauncherWidget selectedWidget;
protected LauncherWidget[] widgets;
protected void MouseMove( object sender, MouseMoveEventArgs e ) {
for( int i = 0; i < widgets.Length; i++ ) {
LauncherWidget widget = widgets[i];
if( e.X >= widget.X && e.Y >= widget.Y &&
e.X < widget.X + widget.Width && e.Y < widget.Y + widget.Height ) {
if( selectedWidget == widget ) return;
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
if( selectedWidget != null )
UnselectWidget( selectedWidget );
SelectWidget( widget );
}
selectedWidget = widget;
return;
}
}
if( selectedWidget == null ) return;
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
UnselectWidget( selectedWidget );
}
selectedWidget = null;
}
protected virtual void UnselectWidget( LauncherWidget widget ) {
}
protected virtual void SelectWidget( LauncherWidget widget ) {
}
protected void MouseButtonDown( object sender, MouseButtonEventArgs e ) {
if( e.Button != MouseButton.Left ) return;
if( selectedWidget != null && selectedWidget.OnClick != null )
selectedWidget.OnClick();
}
}
}

View File

@ -0,0 +1,78 @@
using System;
using System.Drawing;
using ClassicalSharp;
using OpenTK.Input;
namespace Launcher2 {
public sealed class MainScreen : LauncherScreen {
public MainScreen( LauncherWindow game ) : base( game ) {
game.Window.Mouse.Move += MouseMove;
game.Window.Mouse.ButtonDown += MouseButtonDown;
textFont = new Font( "Arial", 16, FontStyle.Bold );
widgets = new LauncherWidget[4];
}
protected override void UnselectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
button.Redraw( game.Drawer, button.Text, textFont );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
Dirty = true;
}
protected override void SelectWidget( LauncherWidget widget ) {
LauncherButtonWidget button = widget as LauncherButtonWidget;
button.Redraw( game.Drawer, button.Text, textFont );
Dirty = true;
}
public override void Init() { Resize(); }
public override void Resize() {
using( IDrawer2D drawer = game.Drawer ) {
drawer.SetBitmap( game.Framebuffer );
DrawButtons( drawer );
}
Dirty = true;
}
Font textFont;
static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 );
void DrawButtons( IDrawer2D drawer ) {
widgetIndex = 0;
MakeButtonAt( drawer, "Direct connect", Anchor.Centre, Anchor.Centre,
buttonWidth, buttonHeight, 0, -100,
() => game.SetScreen( new DirectConnectScreen( game ) ) );
MakeButtonAt( drawer, "ClassiCube.net", Anchor.Centre, Anchor.Centre,
buttonWidth, buttonHeight, 0, -50,
() => game.SetScreen( new ClassiCubeScreen( game ) ) );
MakeButtonAt( drawer, "Singleplayer", Anchor.LeftOrTop, Anchor.BottomOrRight,
sideButtonWidth, buttonHeight, 10, -10,
() => Client.Start( "default.zip" ) );
MakeButtonAt( drawer, "Resume", Anchor.BottomOrRight, Anchor.BottomOrRight,
sideButtonWidth, buttonHeight, -10, -10, null );
}
const int buttonWidth = 220, buttonHeight = 35, sideButtonWidth = 150;
void MakeButtonAt( IDrawer2D drawer, string text, Anchor horAnchor,
Anchor verAnchor, int width, int height, int x, int y, Action onClick ) {
LauncherButtonWidget widget = new LauncherButtonWidget( game );
widget.Text = text;
widget.OnClick = onClick;
widget.DrawAt( drawer, text, textFont, horAnchor, verAnchor, width, height, x, y );
FilterArea( widget.X, widget.Y, widget.Width, widget.Height, 180 );
widgets[widgetIndex++] = widget;
}
public override void Dispose() {
game.Window.Mouse.Move -= MouseMove;
game.Window.Mouse.ButtonDown -= MouseButtonDown;
textFont.Dispose();
}
}
}

View File

@ -1,18 +1,17 @@
using System; using System;
using System.Drawing; using System.Drawing;
using ClassicalSharp; using ClassicalSharp;
using OpenTK;
namespace Launcher2 { namespace Launcher2 {
public sealed class FastButtonWidget { public sealed class LauncherButtonWidget : LauncherWidget {
public int ButtonWidth, ButtonHeight; public int ButtonWidth, ButtonHeight;
public int X, Y, Width, Height;
public NativeWindow Window;
public Action OnClick;
public string Text; public string Text;
public LauncherButtonWidget( LauncherWindow window ) : base( window ) {
}
static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 ); static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 );
public void DrawAt( IDrawer2D drawer, string text, Font font, Anchor horAnchor, public void DrawAt( IDrawer2D drawer, string text, Font font, Anchor horAnchor,
Anchor verAnchor, int width, int height, int x, int y ) { Anchor verAnchor, int width, int height, int x, int y ) {
@ -22,16 +21,6 @@ namespace Launcher2 {
Redraw( drawer, text, font ); Redraw( drawer, text, font );
} }
void CalculateOffset( int x, int y, Anchor horAnchor, Anchor verAnchor ) {
if( horAnchor == Anchor.LeftOrTop ) X = x;
else if( horAnchor == Anchor.Centre ) X = x + Window.Width / 2 - Width / 2;
else if( horAnchor == Anchor.BottomOrRight ) X = x + Window.Width - Width;
if( verAnchor == Anchor.LeftOrTop ) Y = y;
else if( verAnchor == Anchor.Centre ) Y = y + Window.Height / 2 - Height / 2;
else if( verAnchor == Anchor.BottomOrRight ) Y = y + Window.Height - Height;
}
public void Redraw( IDrawer2D drawer, string text, Font font ) { public void Redraw( IDrawer2D drawer, string text, Font font ) {
Size size = drawer.MeasureSize( text, font, true ); Size size = drawer.MeasureSize( text, font, true );
int width = ButtonWidth, height = ButtonHeight; int width = ButtonWidth, height = ButtonHeight;

View File

@ -0,0 +1,55 @@
using System;
using System.Drawing;
using ClassicalSharp;
namespace Launcher2 {
public sealed class LauncherTextInputWidget : LauncherWidget {
public int ButtonWidth, ButtonHeight;
public string Text;
public bool Active;
public bool Password;
public LauncherTextInputWidget( LauncherWindow window ) : base( window ) {
}
public void DrawAt( IDrawer2D drawer, string text, Font font,
Anchor horAnchor, Anchor verAnchor, int width, int height, int x, int y ) {
ButtonWidth = width; ButtonHeight = height;
Width = width; Height = height;
CalculateOffset( x, y, horAnchor, verAnchor );
Redraw( drawer, text, font );
}
public void Redraw( IDrawer2D drawer, string text, Font font ) {
Text = text;
if( Password )
text = new String( '*', text.Length );
Size size = drawer.MeasureSize( text, font, true );
Width = Math.Max( ButtonWidth, size.Width + 7 );
FastColour col = Active ? FastColour.White : new FastColour( 160, 160, 160 );
drawer.DrawRectBounds( col, 2, X, Y, Width, Height );
drawer.DrawRect( FastColour.Black, X + 2, Y + 2, Width - 4, Height - 4 );
DrawTextArgs args = new DrawTextArgs( text, true );
args.SkipPartsCheck = true;
drawer.DrawText( font, ref args, X + 7, Y + 2 );
}
public void AddChar( char c, Font font ) {
if( c >= ' ' && c <= '~' ) {
Text += c;
Redraw( Window.Drawer, Text, font );
}
}
public void RemoveChar( Font font ) {
if( Text.Length == 0 ) return;
Text = Text.Substring( 0, Text.Length - 1 );
Redraw( Window.Drawer, Text, font );
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Drawing;
using ClassicalSharp;
namespace Launcher2 {
public sealed class LauncherTextWidget : LauncherWidget {
public string Text;
public LauncherTextWidget( LauncherWindow window ) : base( window ) {
}
public void DrawAt( IDrawer2D drawer, string text, Font font,
Anchor horAnchor, Anchor verAnchor, int x, int y ) {
Size size = drawer.MeasureSize( text, font, true );
Width = size.Width; Height = size.Height;
CalculateOffset( x, y, horAnchor, verAnchor );
Redraw( drawer, text, font );
}
public void Redraw( IDrawer2D drawer, string text, Font font ) {
DrawTextArgs args = new DrawTextArgs( text, true );
drawer.DrawText( font, ref args, X, Y );
}
}
}

View File

@ -0,0 +1,26 @@
using System;
using ClassicalSharp;
namespace Launcher2 {
public abstract class LauncherWidget {
public int X, Y, Width, Height;
public LauncherWindow Window;
public Action OnClick;
public LauncherWidget( LauncherWindow window ) {
Window = window;
}
protected void CalculateOffset( int x, int y, Anchor horAnchor, Anchor verAnchor ) {
if( horAnchor == Anchor.LeftOrTop ) X = x;
else if( horAnchor == Anchor.Centre ) X = x + Window.Width / 2 - Width / 2;
else if( horAnchor == Anchor.BottomOrRight ) X = x + Window.Width - Width;
if( verAnchor == Anchor.LeftOrTop ) Y = y;
else if( verAnchor == Anchor.Centre ) Y = y + Window.Height / 2 - Height / 2;
else if( verAnchor == Anchor.BottomOrRight ) Y = y + Window.Height - Height;
}
}
}

View File

@ -45,16 +45,27 @@
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Security" />
<Reference Include="System.Windows.Forms" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="FastButtonWidget.cs" /> <Compile Include="Client.cs" />
<Compile Include="GameStartData.cs" /> <Compile Include="GameStartData.cs" />
<Compile Include="MainScreen.cs" /> <Compile Include="Gui\ClassiCubeScreen.cs" />
<Compile Include="Gui\DirectConnectScreen.cs" />
<Compile Include="Gui\LauncherScreen.cs" />
<Compile Include="Gui\MainScreen.cs" />
<Compile Include="Gui\Widgets\LauncherButtonWidget.cs" />
<Compile Include="Gui\Widgets\LauncherTextInputWidget.cs" />
<Compile Include="Gui\Widgets\LauncherTextWidget.cs" />
<Compile Include="Gui\Widgets\LauncherWidget.cs" />
<Compile Include="LauncherWindow.cs" />
<Compile Include="Patcher\Animations.cs" /> <Compile Include="Patcher\Animations.cs" />
<Compile Include="Patcher\ResourceFetcher.cs" /> <Compile Include="Patcher\ResourceFetcher.cs" />
<Compile Include="Patcher\ZipWriter.cs" /> <Compile Include="Patcher\ZipWriter.cs" />
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Secure.cs" />
<Compile Include="WebService\ClassiCubeSession.cs" /> <Compile Include="WebService\ClassiCubeSession.cs" />
<Compile Include="WebService\GameSession.cs" /> <Compile Include="WebService\GameSession.cs" />
<Compile Include="WebService\ServerListEntry.cs" /> <Compile Include="WebService\ServerListEntry.cs" />
@ -71,6 +82,8 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Gui" />
<Folder Include="Gui\Widgets" />
<Folder Include="Patcher" /> <Folder Include="Patcher" />
<Folder Include="WebService" /> <Folder Include="WebService" />
</ItemGroup> </ItemGroup>

110
Launcher2/LauncherWindow.cs Normal file
View File

@ -0,0 +1,110 @@
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Threading;
using ClassicalSharp;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Platform.Windows;
namespace Launcher2 {
public sealed class LauncherWindow {
public NativeWindow Window;
public IDrawer2D Drawer;
public LauncherScreen screen;
public bool Dirty;
public int Width { get { return Window.Width; } }
public int Height { get { return Window.Height; } }
public Bitmap Framebuffer;
Font logoFont, logoItalicFont;
public void Init() {
Window.Resize += Resize;
Window.FocusedChanged += FocusedChanged;
logoFont = new Font( "Times New Roman", 28, FontStyle.Bold );
logoItalicFont = new Font( "Times New Roman", 28, FontStyle.Italic );
}
void FocusedChanged( object sender, EventArgs e ) {
MakeBackground();
screen.Resize();
}
void Resize( object sender, EventArgs e ) {
MakeBackground();
screen.Resize();
}
public void SetScreen( LauncherScreen screen ) {
if( this.screen != null )
this.screen.Dispose();
MakeBackground();
this.screen = screen;
screen.Init();
}
public void Run() {
Window = new NativeWindow( 480, 480, Program.AppName, 0,
GraphicsMode.Default, DisplayDevice.Default );
Window.Visible = true;
screen = new MainScreen( this );
Drawer = new GdiPlusDrawer2D( null );
Init();
SetScreen( new MainScreen( this ) );
while( true ) {
Window.ProcessEvents();
if( !Window.Exists ) break;
if( Dirty || (screen != null && screen.Dirty) )
Display();
Thread.Sleep( 1 );
}
}
void Display() {
Dirty = false;
if( screen != null )
screen.Dirty = false;
WinWindowInfo info = (WinWindowInfo)Window.WindowInfo;
IntPtr dc = info.DeviceContext;
using( Graphics g = Graphics.FromHdc( dc ) )
g.DrawImage( Framebuffer, 0, 0, Framebuffer.Width, Framebuffer.Height );
}
internal static FastColour clearColour = new FastColour( 30, 30, 30 );
public void MakeBackground() {
if( Framebuffer != null )
Framebuffer.Dispose();
Framebuffer = new Bitmap( Width, Height );
using( IDrawer2D drawer = Drawer ) {
drawer.SetBitmap( Framebuffer );
drawer.Clear( clearColour );
Size size1 = drawer.MeasureSize( "&eClassical", logoItalicFont, true );
Size size2 = drawer.MeasureSize( "&eSharp", logoFont, true );
int xStart = Width / 2 - (size1.Width + size2.Width ) / 2;
DrawTextArgs args = new DrawTextArgs( "&eClassical", true );
drawer.DrawText( logoItalicFont, ref args, xStart, 20 );
args.Text = "&eSharp";
drawer.DrawText( logoFont, ref args, xStart + size1.Width, 20 );
}
Dirty = true;
}
public void Dispose() {
logoFont.Dispose();
logoItalicFont.Dispose();
}
}
}

View File

@ -1,156 +0,0 @@
using System;
using System.Drawing;
using ClassicalSharp;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Platform.Windows;
using OpenTK.Platform;
using OpenTK.Input;
namespace Launcher2 {
public sealed class MainScreen {
public IDrawer2D Drawer;
public NativeWindow Window;
public void Init() {
Window.Resize += HandleResize;
Window.Mouse.Move += MouseMove;
Window.Mouse.ButtonDown += MouseButtonDown;
logoFont = new Font( "Times New Roman", 28, FontStyle.Bold );
logoItalicFont = new Font( "Times New Roman", 28, FontStyle.Italic );
textFont = new Font( "Arial", 16, FontStyle.Bold );
}
FastButtonWidget selectedWidget;
void MouseMove( object sender, MouseMoveEventArgs e ) {
//System.Diagnostics.Debug.Print( "moved" );
for( int i = 0; i < widgets.Length; i++ ) {
FastButtonWidget widget = widgets[i];
if( e.X >= widget.X && e.Y >= widget.Y &&
e.X < widget.X + widget.Width && e.Y < widget.Y + widget.Height ) {
if( selectedWidget != widget && selectedWidget != null ) {
using( IDrawer2D drawer = Drawer ) {
drawer.SetBitmap( background );
selectedWidget.Redraw( Drawer, selectedWidget.Text, textFont );
FilterButton( selectedWidget.X, selectedWidget.Y,
selectedWidget.Width, selectedWidget.Height, 180 );
widget.Redraw( Drawer, widget.Text, textFont );
}
}
selectedWidget = widget;
break;
}
}
}
void MouseButtonDown( object sender, MouseButtonEventArgs e ) {
if( e.Button != MouseButton.Left ) return;
if( selectedWidget != null && selectedWidget.OnClick != null )
selectedWidget.OnClick();
}
public void Display() {
WinWindowInfo info = ((WinWindowInfo)Window.WindowInfo);
IntPtr dc = info.DeviceContext;
using( Graphics g = Graphics.FromHdc( dc ) ) {
g.DrawImage( background, 0, 0, background.Width, background.Height );
}
}
Bitmap background;
Font textFont, logoFont, logoItalicFont;
static FastColour clearColour = new FastColour( 30, 30, 30 );
static uint clearColourBGRA = (uint)(new FastColour( 30, 30, 30 ).ToArgb());
public void RecreateBackground() {
System.Diagnostics.Debug.Print( "DISPLAY" );
if( background != null )
background.Dispose();
background = new Bitmap( Window.Width, Window.Height );
using( IDrawer2D drawer = Drawer ) {
drawer.SetBitmap( background );
drawer.Clear( clearColour );
Size size1 = drawer.MeasureSize( "&eClassical", logoItalicFont, true );
Size size2 = drawer.MeasureSize( "&eSharp", logoFont, true );
int xStart = Window.Width / 2 - (size1.Width + size2.Width ) / 2;
DrawTextArgs args = new DrawTextArgs( "&eClassical", true );
drawer.DrawText( logoItalicFont, ref args, xStart, 20 );
args.Text = "&eSharp";
drawer.DrawText( logoFont, ref args, xStart + size1.Width, 20 );
DrawButtons( drawer );
}
logoFont.Dispose();
logoItalicFont.Dispose();
}
static FastColour boxCol = new FastColour( 169, 143, 192 ), shadowCol = new FastColour( 97, 81, 110 );
void DrawButtons( IDrawer2D drawer ) {
widgetIndex = 0;
MakeButtonAt( drawer, "Direct connect", Anchor.Centre, Anchor.Centre,
buttonWidth, buttonHeight, 0, -100 );
MakeButtonAt( drawer, "ClassiCube.net", Anchor.Centre, Anchor.Centre,
buttonWidth, buttonHeight, 0, -50 );
MakeButtonAt( drawer, "Default texture pack", Anchor.Centre, Anchor.Centre,
buttonWidth, buttonHeight, 0, 50 );
MakeButtonAt( drawer, "Singleplayer", Anchor.LeftOrTop, Anchor.BottomOrRight,
sideButtonWidth, buttonHeight, 10, -10 );
widgets[widgetIndex - 1].OnClick
= () => Program.StartClient( "default.zip" );
MakeButtonAt( drawer, "Resume", Anchor.BottomOrRight, Anchor.BottomOrRight,
sideButtonWidth, buttonHeight, -10, -10 );
}
FastButtonWidget[] widgets = new FastButtonWidget[5];
int widgetIndex = 0;
const int buttonWidth = 220, buttonHeight = 35, sideButtonWidth = 150;
void MakeButtonAt( IDrawer2D drawer, string text, Anchor horAnchor,
Anchor verAnchor, int width, int height, int x, int y ) {
FastButtonWidget widget = new FastButtonWidget();
widget.Window = Window;
widget.Text = text;
widget.DrawAt( drawer, text, textFont, horAnchor, verAnchor, width, height, x, y );
FilterButton( widget.X, widget.Y, widget.Width, widget.Height, 180 );
widgets[widgetIndex++] = widget;
}
void HandleResize( object sender, EventArgs e ) {
RecreateBackground();
}
public void Dispose() {
logoFont.Dispose();
logoItalicFont.Dispose();
textFont.Dispose();
}
unsafe void FilterButton( int x, int y, int width, int height, byte scale ) {
using( FastBitmap bmp = new FastBitmap( background, true ) ) {
for( int yy = y; yy < y + height; yy++ ) {
int* row = bmp.GetRowPtr( yy ) + x;
for( int xx = 0; xx < width; xx++ ) {
uint pixel = (uint)row[xx];
if( pixel == clearColourBGRA ) continue;
uint a = pixel & 0xFF000000;
uint r = (pixel >> 16) & 0xFF;
uint g = (pixel >> 8) & 0xFF;
uint b = pixel & 0xFF;
r = (r * scale) / 255;
g = (g * scale) / 255;
b = (b * scale) / 255;
row[xx] = (int)(a | (r << 16) | (g << 8) | b);
}
}
}
}
}
}

View File

@ -1,9 +1,7 @@
using System; using System;
using ClassicalSharp;
using OpenTK;
using OpenTK.Graphics;
using System.IO;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace Launcher2 { namespace Launcher2 {
@ -13,43 +11,43 @@ namespace Launcher2 {
[STAThread] [STAThread]
private static void Main( string[] args ) { private static void Main( string[] args ) {
NativeWindow window = new NativeWindow( 480, 480, AppName, 0, if( !Debugger.IsAttached )
GraphicsMode.Default, DisplayDevice.Default ); AppDomain.CurrentDomain.UnhandledException += UnhandledException;
window.Visible = true;
MainScreen screen = new MainScreen(); LauncherWindow window = new LauncherWindow();
screen.Drawer = new GdiPlusDrawer2D( null ); window.Run();
screen.Window = window;
screen.Init();
screen.RecreateBackground();
while( true ) {
window.ProcessEvents();
if( !window.Exists ) break;
screen.Display();
System.Threading.Thread.Sleep( 10 );
}
} }
static string missingExeMessage = "Failed to start ClassicalSharp. (classicalsharp.exe was not found)" static void UnhandledException( object sender, UnhandledExceptionEventArgs e ) {
+ Environment.NewLine + Environment.NewLine + // So we don't get the normal unhelpful crash dialog on Windows.
"This application is only the launcher, it is not the actual client. " + Exception ex = (Exception)e.ExceptionObject;
"Please place the launcher in the same directory as the client (classicalsharp.exe)."; bool wroteToCrashLog = LogException( ex );
string error = wroteToCrashLog ? null :
(ex.GetType().FullName + ": " + ex.Message + Environment.NewLine + ex.StackTrace);
public static bool StartClient( string args ) { string message = wroteToCrashLog ?
Process process = null; "The cause of the crash has been logged to \"crash-launcher.log\". Please consider reporting the crash " +
"(and the circumstances that caused it) to github.com/UnknownShadow200/ClassicalSharp/issues" :
if( !File.Exists( "ClassicalSharp.exe" ) ) { "Failed to write the cause of the crash to \"crash-launcher.log\". Please consider reporting the crash " +
// TODO: show message popup "(and the circumstances that caused it) to github.com/UnknownShadow200/ClassicalSharp/issues" +
Environment.NewLine + Environment.NewLine + error;
MessageBox.Show( "Oh dear. ClassicalSharp has crashed." + Environment.NewLine + Environment.NewLine + message, "ClassicalSharp has crashed" );
Environment.Exit( 1 );
}
internal static bool LogException( Exception ex ) {
string error = ex.GetType().FullName + ": " + ex.Message + Environment.NewLine + ex.StackTrace;
try {
using( StreamWriter writer = new StreamWriter( "crash_launcher.log", true ) ) {
writer.WriteLine( "Crash time: " + DateTime.Now.ToString() );
writer.WriteLine( error );
writer.WriteLine();
}
} catch( Exception ) {
return false; return false;
} }
if( Type.GetType( "Mono.Runtime" ) != null ) {
process = Process.Start( "mono", "\"ClassicalSharp.exe\" " + args );
} else {
process = Process.Start( "ClassicalSharp.exe", args );
}
return true; return true;
} }
} }

52
Launcher2/Secure.cs Normal file
View File

@ -0,0 +1,52 @@
using System;
using System.Security.Cryptography;
namespace Launcher2 {
public static class Secure {
public static string Encode( string decoded, string key ) {
if( String.IsNullOrEmpty( decoded ) || String.IsNullOrEmpty( key ) ) return "";
byte[] data = new byte[decoded.Length];
for( int i = 0; i < decoded.Length; i++ )
data[i] = (byte)decoded[i];
try {
byte[] v2 = ProtectedData.Protect( data, null, DataProtectionScope.CurrentUser );
return Convert.ToBase64String( v2 );
} catch {
// XORs data. *NOT* very secure, only designed to prevent reading from options.txt.
for( int i = 0; i < data.Length; i++ )
data[i] = (byte)(data[i] ^ key[i % key.Length] ^ 0x43);
return Convert.ToBase64String( data );
}
}
public static string Decode( string encoded, string key ) {
if( String.IsNullOrEmpty( encoded ) || String.IsNullOrEmpty( key ) ) return null;
byte[] data;
try {
data = Convert.FromBase64String( encoded );
} catch( FormatException ) {
return null;
}
try {
data = ProtectedData.Unprotect( data, null, DataProtectionScope.CurrentUser );
char[] c = new char[data.Length];
for( int i = 0; i < c.Length; i++ )
c[i] = (char)data[i];
return new String( c );
} catch {
if( encoded.Length > 64 || data.Length > 64 ) return null;
char[] c = new char[data.Length];
for( int i = 0; i < c.Length; i++ )
c[i] = (char)(data[i] ^ key[i % key.Length] ^ 0x43);
return new String( c );
}
}
}
}