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 );
}
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() {
g.Dispose();
g = null;

View File

@ -35,6 +35,9 @@ namespace ClassicalSharp {
/// <summary> Clears the entire bound bitmap to the specified colour. </summary>
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>
public abstract void Dispose();

View File

@ -11,9 +11,8 @@ namespace ClassicalSharp {
[STAThread]
public static void Main( string[] args ) {
if( !Debugger.IsAttached ) {
if( !Debugger.IsAttached )
AppDomain.CurrentDomain.UnhandledException += UnhandledException;
}
Utils.Log( "Starting " + Utils.AppName + ".." );
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.Drawing;
using ClassicalSharp;
using OpenTK;
namespace Launcher2 {
public sealed class FastButtonWidget {
public sealed class LauncherButtonWidget : LauncherWidget {
public int ButtonWidth, ButtonHeight;
public int X, Y, Width, Height;
public NativeWindow Window;
public Action OnClick;
public string Text;
public LauncherButtonWidget( LauncherWindow window ) : base( window ) {
}
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,
Anchor verAnchor, int width, int height, int x, int y ) {
@ -22,16 +21,6 @@ namespace Launcher2 {
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 ) {
Size size = drawer.MeasureSize( text, font, true );
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>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Security" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="FastButtonWidget.cs" />
<Compile Include="Client.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\ResourceFetcher.cs" />
<Compile Include="Patcher\ZipWriter.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Secure.cs" />
<Compile Include="WebService\ClassiCubeSession.cs" />
<Compile Include="WebService\GameSession.cs" />
<Compile Include="WebService\ServerListEntry.cs" />
@ -71,6 +82,8 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Gui" />
<Folder Include="Gui\Widgets" />
<Folder Include="Patcher" />
<Folder Include="WebService" />
</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 ClassicalSharp;
using OpenTK;
using OpenTK.Graphics;
using System.IO;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;
namespace Launcher2 {
@ -13,43 +11,43 @@ namespace Launcher2 {
[STAThread]
private static void Main( string[] args ) {
NativeWindow window = new NativeWindow( 480, 480, AppName, 0,
GraphicsMode.Default, DisplayDevice.Default );
window.Visible = true;
if( !Debugger.IsAttached )
AppDomain.CurrentDomain.UnhandledException += UnhandledException;
MainScreen screen = new MainScreen();
screen.Drawer = new GdiPlusDrawer2D( null );
screen.Window = window;
screen.Init();
screen.RecreateBackground();
while( true ) {
window.ProcessEvents();
if( !window.Exists ) break;
screen.Display();
System.Threading.Thread.Sleep( 10 );
}
LauncherWindow window = new LauncherWindow();
window.Run();
}
static string missingExeMessage = "Failed to start ClassicalSharp. (classicalsharp.exe was not found)"
+ Environment.NewLine + Environment.NewLine +
"This application is only the launcher, it is not the actual client. " +
"Please place the launcher in the same directory as the client (classicalsharp.exe).";
static void UnhandledException( object sender, UnhandledExceptionEventArgs e ) {
// So we don't get the normal unhelpful crash dialog on Windows.
Exception ex = (Exception)e.ExceptionObject;
bool wroteToCrashLog = LogException( ex );
string error = wroteToCrashLog ? null :
(ex.GetType().FullName + ": " + ex.Message + Environment.NewLine + ex.StackTrace);
public static bool StartClient( string args ) {
Process process = null;
string message = wroteToCrashLog ?
"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" ) ) {
// TODO: show message popup
"Failed to write the cause of the crash to \"crash-launcher.log\". Please consider reporting the crash " +
"(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;
}
if( Type.GetType( "Mono.Runtime" ) != null ) {
process = Process.Start( "mono", "\"ClassicalSharp.exe\" " + args );
} else {
process = Process.Start( "ClassicalSharp.exe", args );
}
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 );
}
}
}
}