Fully implement the login screen (Closes #6)
This commit is contained in:
parent
b418625a48
commit
ae2703418b
|
@ -16,6 +16,7 @@
|
|||
|
||||
use openssl;
|
||||
use serde_json;
|
||||
use hyper;
|
||||
|
||||
pub mod mojang;
|
||||
|
||||
|
@ -633,6 +634,8 @@ pub enum Error {
|
|||
Err(String),
|
||||
Disconnect(format::Component),
|
||||
IOError(io::Error),
|
||||
Json(serde_json::Error),
|
||||
Hyper(hyper::Error),
|
||||
}
|
||||
|
||||
impl convert::From<io::Error> for Error {
|
||||
|
@ -641,12 +644,26 @@ impl convert::From<io::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl convert::From<serde_json::Error> for Error {
|
||||
fn from(e: serde_json::Error) -> Error {
|
||||
Error::Json(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl convert::From<hyper::Error> for Error {
|
||||
fn from(e: hyper::Error) -> Error {
|
||||
Error::Hyper(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
Error::Err(ref val) => &val[..],
|
||||
Error::Disconnect(_) => "Disconnect",
|
||||
Error::IOError(ref e) => e.description(),
|
||||
Error::Json(ref e) => e.description(),
|
||||
Error::Hyper(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -657,6 +674,8 @@ impl ::std::fmt::Display for Error {
|
|||
Error::Err(ref val) => write!(f, "protocol error: {}", val),
|
||||
Error::Disconnect(ref val) => write!(f, "{}", val),
|
||||
Error::IOError(ref e) => e.fmt(f),
|
||||
Error::Json(ref e) => e.fmt(f),
|
||||
Error::Hyper(ref e) => e.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
|
||||
use openssl;
|
||||
use serde_json;
|
||||
use serde_json::builder::ObjectBuilder;
|
||||
use hyper;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Profile {
|
||||
pub username: String,
|
||||
pub id: String,
|
||||
|
@ -23,9 +25,85 @@ pub struct Profile {
|
|||
}
|
||||
|
||||
const JOIN_URL: &'static str = "https://sessionserver.mojang.com/session/minecraft/join";
|
||||
const LOGIN_URL: &'static str = "https://authserver.mojang.com/authenticate";
|
||||
const REFRESH_URL: &'static str = "https://authserver.mojang.com/refresh";
|
||||
const VALIDATE_URL: &'static str = "https://authserver.mojang.com/validate";
|
||||
|
||||
impl Profile {
|
||||
pub fn join_server(&self, server_id: &String, shared_key: &Vec<u8>, public_key: &Vec<u8>) {
|
||||
pub fn login(username: &str, password: &str, token: &str) -> Result<Profile, super::Error> {
|
||||
let req_msg = ObjectBuilder::new()
|
||||
.insert("username", username)
|
||||
.insert("password", password)
|
||||
.insert("clientToken", token)
|
||||
.insert_object("agent", |b| b
|
||||
.insert("name", "Minecraft")
|
||||
.insert("version", 1)
|
||||
)
|
||||
.unwrap();
|
||||
let req = try!(serde_json::to_string(&req_msg));
|
||||
|
||||
let client = hyper::Client::new();
|
||||
let res = try!(client.post(LOGIN_URL)
|
||||
.body(&req)
|
||||
.header(hyper::header::ContentType("application/json".parse().unwrap()))
|
||||
.send());
|
||||
|
||||
let ret: serde_json::Value = try!(serde_json::from_reader(res));
|
||||
if let Some(error) = ret.find("error").and_then(|v| v.as_string()) {
|
||||
return Err(super::Error::Err(format!(
|
||||
"{}: {}",
|
||||
error,
|
||||
ret.find("errorMessage").and_then(|v| v.as_string()).unwrap())
|
||||
));
|
||||
}
|
||||
Ok(Profile {
|
||||
username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn refresh(self, token: &str) -> Result<Profile, super::Error> {
|
||||
let req_msg = ObjectBuilder::new()
|
||||
.insert("accessToken", self.access_token.clone())
|
||||
.insert("clientToken", token)
|
||||
.unwrap();
|
||||
let req = try!(serde_json::to_string(&req_msg));
|
||||
|
||||
let client = hyper::Client::new();
|
||||
let res = try!(client.post(VALIDATE_URL)
|
||||
.body(&req)
|
||||
.header(hyper::header::ContentType("application/json".parse().unwrap()))
|
||||
.send());
|
||||
|
||||
if res.status != hyper::status::StatusCode::NoContent {
|
||||
println!("Rerefeshing");
|
||||
// Refresh needed
|
||||
let res = try!(client.post(REFRESH_URL)
|
||||
.body(&req)
|
||||
.header(hyper::header::ContentType("application/json".parse().unwrap()))
|
||||
.send());
|
||||
|
||||
let ret: serde_json::Value = try!(serde_json::from_reader(res));
|
||||
if let Some(error) = ret.find("error").and_then(|v| v.as_string()) {
|
||||
return Err(super::Error::Err(format!(
|
||||
"{}: {}",
|
||||
error,
|
||||
ret.find("errorMessage").and_then(|v| v.as_string()).unwrap())
|
||||
));
|
||||
}
|
||||
return Ok(Profile {
|
||||
username: ret.lookup("selectedProfile.name").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
id: ret.lookup("selectedProfile.id").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
access_token: ret.find("accessToken").and_then(|v| v.as_string()).unwrap().to_owned(),
|
||||
});
|
||||
}
|
||||
println!("Reuse");
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
// TODO
|
||||
pub fn join_server(&self, server_id: &str, shared_key: &Vec<u8>, public_key: &Vec<u8>) {
|
||||
let mut sha1 = openssl::SHA1::new();
|
||||
sha1.update(server_id.as_bytes());
|
||||
sha1.update(&shared_key[..]);
|
||||
|
@ -46,7 +124,7 @@ impl Profile {
|
|||
hash_val.to_owned()
|
||||
};
|
||||
|
||||
let join_msg = serde_json::builder::ObjectBuilder::new()
|
||||
let join_msg = ObjectBuilder::new()
|
||||
.insert("accessToken", &self.access_token)
|
||||
.insert("selectedProfile", &self.id)
|
||||
.insert("serverId", hash_str)
|
||||
|
@ -66,6 +144,10 @@ impl Profile {
|
|||
};
|
||||
panic!("{:?}", ret);
|
||||
}
|
||||
|
||||
pub fn is_complete(&self) -> bool {
|
||||
!self.username.is_empty() && !self.id.is_empty() && !self.access_token.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
fn twos_compliment(data: &mut Vec<u8>) {
|
||||
|
|
|
@ -93,9 +93,10 @@ impl super::Screen for Connecting {
|
|||
fn tick(&mut self,
|
||||
_delta: f64,
|
||||
renderer: &mut render::Renderer,
|
||||
ui_container: &mut ui::Container) {
|
||||
ui_container: &mut ui::Container) -> Option<Box<super::Screen>>{
|
||||
let elements = self.elements.as_mut().unwrap();
|
||||
|
||||
elements.logo.tick(renderer, ui_container);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,19 @@
|
|||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
|
||||
use rand::{self, Rng};
|
||||
|
||||
use ui;
|
||||
use render;
|
||||
use console;
|
||||
use protocol;
|
||||
use protocol::mojang;
|
||||
use auth;
|
||||
|
||||
pub struct Login {
|
||||
elements: Option<UIElements>,
|
||||
|
@ -26,6 +36,17 @@ pub struct Login {
|
|||
struct UIElements {
|
||||
logo: ui::logo::Logo,
|
||||
elements: ui::Collection,
|
||||
|
||||
login_btn: ui::ElementRef<ui::Button>,
|
||||
login_btn_text: ui::ElementRef<ui::Text>,
|
||||
login_error: ui::ElementRef<ui::Text>,
|
||||
username_txt: ui::ElementRef<ui::TextBox>,
|
||||
password_txt: ui::ElementRef<ui::TextBox>,
|
||||
try_login: Rc<Cell<bool>>,
|
||||
refresh: bool,
|
||||
login_res: Option<mpsc::Receiver<Result<mojang::Profile, protocol::Error>>>,
|
||||
|
||||
profile: mojang::Profile,
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,6 +61,8 @@ impl super::Screen for Login {
|
|||
let logo = ui::logo::Logo::new(renderer.resources.clone(), renderer, ui_container);
|
||||
let mut elements = ui::Collection::new();
|
||||
|
||||
let try_login = Rc::new(Cell::new(false));
|
||||
|
||||
// Login
|
||||
let (mut login, mut txt) = super::new_button_text(renderer, "Login", 0.0, 100.0, 400.0, 40.0);
|
||||
login.set_v_attach(ui::VAttach::Middle);
|
||||
|
@ -47,15 +70,21 @@ impl super::Screen for Login {
|
|||
let re = ui_container.add(login);
|
||||
txt.set_parent(&re);
|
||||
let tre = ui_container.add(txt);
|
||||
let tl = try_login.clone();
|
||||
super::button_action(ui_container,
|
||||
re.clone(),
|
||||
Some(tre.clone()),
|
||||
|game, _| {
|
||||
game.screen_sys
|
||||
.replace_screen(Box::new(super::ServerList::new(None)));
|
||||
});
|
||||
elements.add(re);
|
||||
elements.add(tre);
|
||||
move |_, _| {
|
||||
tl.set(true);
|
||||
});
|
||||
let login_btn = elements.add(re);
|
||||
let login_btn_text = elements.add(tre);
|
||||
|
||||
// Login Error
|
||||
let mut login_error = ui::Text::new(renderer, "", 0.0, 150.0, 255, 50, 50);
|
||||
login_error.set_v_attach(ui::VAttach::Middle);
|
||||
login_error.set_h_attach(ui::HAttach::Center);
|
||||
let login_error = elements.add(ui_container.add(login_error));
|
||||
|
||||
// Username
|
||||
let mut username = ui::TextBox::new(renderer, "", 0.0, -20.0, 400.0, 40.0);
|
||||
|
@ -67,7 +96,7 @@ impl super::Screen for Login {
|
|||
let ure = ui_container.add(username);
|
||||
let mut username_label = ui::Text::new(renderer, "Username/Email:", 0.0, -18.0, 255, 255, 255);
|
||||
username_label.set_parent(&ure);
|
||||
elements.add(ure);
|
||||
let username_txt = elements.add(ure);
|
||||
elements.add(ui_container.add(username_label));
|
||||
|
||||
// Password
|
||||
|
@ -75,13 +104,14 @@ impl super::Screen for Login {
|
|||
password.set_v_attach(ui::VAttach::Middle);
|
||||
password.set_h_attach(ui::HAttach::Center);
|
||||
password.set_password(true);
|
||||
password.add_submit_func(|_, _| {
|
||||
println!("Submit!");
|
||||
let tl = try_login.clone();
|
||||
password.add_submit_func(move |_, _| {
|
||||
tl.set(true);
|
||||
});
|
||||
let pre = ui_container.add(password);
|
||||
let mut password_label = ui::Text::new(renderer, "Password:", 0.0, -18.0, 255, 255, 255);
|
||||
password_label.set_parent(&pre);
|
||||
elements.add(pre);
|
||||
let password_txt = elements.add(pre);
|
||||
elements.add(ui_container.add(password_label));
|
||||
|
||||
// Disclaimer
|
||||
|
@ -96,9 +126,28 @@ impl super::Screen for Login {
|
|||
warn.set_h_attach(ui::HAttach::Right);
|
||||
elements.add(ui_container.add(warn));
|
||||
|
||||
let console = self.console.lock().unwrap();
|
||||
let profile = mojang::Profile {
|
||||
username: console.get(auth::CL_USERNAME).clone(),
|
||||
id: console.get(auth::CL_UUID).clone(),
|
||||
access_token: console.get(auth::AUTH_TOKEN).clone(),
|
||||
};
|
||||
let refresh = profile.is_complete();
|
||||
try_login.set(refresh);
|
||||
|
||||
self.elements = Some(UIElements {
|
||||
logo: logo,
|
||||
elements: elements,
|
||||
profile: profile,
|
||||
login_btn: login_btn,
|
||||
login_btn_text: login_btn_text,
|
||||
login_error: login_error,
|
||||
try_login: try_login,
|
||||
refresh: refresh,
|
||||
login_res: None,
|
||||
|
||||
username_txt: username_txt,
|
||||
password_txt: password_txt,
|
||||
});
|
||||
}
|
||||
fn on_deactive(&mut self, _renderer: &mut render::Renderer, ui_container: &mut ui::Container) {
|
||||
|
@ -114,9 +163,79 @@ impl super::Screen for Login {
|
|||
fn tick(&mut self,
|
||||
_delta: f64,
|
||||
renderer: &mut render::Renderer,
|
||||
ui_container: &mut ui::Container) {
|
||||
ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
|
||||
let elements = self.elements.as_mut().unwrap();
|
||||
|
||||
if elements.try_login.get() && elements.login_res.is_none() {
|
||||
elements.try_login.set(false);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
elements.login_res = Some(rx);
|
||||
{
|
||||
let btn = ui_container.get_mut(&elements.login_btn);
|
||||
btn.set_disabled(true);
|
||||
}
|
||||
{
|
||||
let txt = ui_container.get_mut(&elements.login_btn_text);
|
||||
txt.set_text(renderer, "Logging in...");
|
||||
}
|
||||
let mut console = self.console.lock().unwrap();
|
||||
let mut client_token = console.get(auth::AUTH_CLIENT_TOKEN).clone();
|
||||
if client_token.is_empty() {
|
||||
client_token = rand::thread_rng().gen_ascii_chars().take(20).collect::<String>();
|
||||
console.set(auth::AUTH_CLIENT_TOKEN, client_token);
|
||||
}
|
||||
let client_token = console.get(auth::AUTH_CLIENT_TOKEN).clone();
|
||||
let username = {
|
||||
let txt = ui_container.get(&elements.username_txt);
|
||||
txt.get_input()
|
||||
};
|
||||
let password = {
|
||||
let txt = ui_container.get(&elements.password_txt);
|
||||
txt.get_input()
|
||||
};
|
||||
let refresh = elements.refresh;
|
||||
let profile = elements.profile.clone();
|
||||
thread::spawn(move || {
|
||||
if refresh {
|
||||
tx.send(profile.refresh(&client_token)).unwrap();
|
||||
} else {
|
||||
tx.send(mojang::Profile::login(&username, &password, &client_token)).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
let mut done = false;
|
||||
if let Some(rx) = elements.login_res.as_ref() {
|
||||
if let Ok(res) = rx.try_recv() {
|
||||
done = true;
|
||||
{
|
||||
let btn = ui_container.get_mut(&elements.login_btn);
|
||||
btn.set_disabled(false);
|
||||
}
|
||||
{
|
||||
let txt = ui_container.get_mut(&elements.login_btn_text);
|
||||
txt.set_text(renderer, "Login");
|
||||
}
|
||||
match res {
|
||||
Ok(val) => {
|
||||
let mut console = self.console.lock().unwrap();
|
||||
console.set(auth::CL_USERNAME, val.username.clone());
|
||||
console.set(auth::CL_UUID, val.id.clone());
|
||||
console.set(auth::AUTH_TOKEN, val.access_token.clone());
|
||||
elements.profile = val;
|
||||
return Some(Box::new(super::ServerList::new(None)));
|
||||
},
|
||||
Err(err) => {
|
||||
let login_error = ui_container.get_mut(&elements.login_error);
|
||||
login_error.set_text(renderer, &format!("{}", err));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
if done {
|
||||
elements.login_res = None;
|
||||
}
|
||||
|
||||
elements.logo.tick(renderer, ui_container);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ pub trait Screen {
|
|||
fn tick(&mut self,
|
||||
delta: f64,
|
||||
renderer: &mut render::Renderer,
|
||||
ui_container: &mut ui::Container);
|
||||
ui_container: &mut ui::Container) -> Option<Box<Screen>>;
|
||||
|
||||
// Events
|
||||
fn on_scroll(&mut self, x: f64, y: f64) {
|
||||
|
@ -106,18 +106,22 @@ impl ScreenSystem {
|
|||
screen.screen.on_deactive(renderer, ui_container);
|
||||
}
|
||||
}
|
||||
let current = self.screens.last_mut().unwrap();
|
||||
if !current.init {
|
||||
current.init = true;
|
||||
current.screen.init(renderer, ui_container);
|
||||
}
|
||||
if !current.active {
|
||||
current.active = true;
|
||||
current.screen.on_active(renderer, ui_container);
|
||||
}
|
||||
|
||||
let swap = {
|
||||
let current = self.screens.last_mut().unwrap();
|
||||
if !current.init {
|
||||
current.init = true;
|
||||
current.screen.init(renderer, ui_container);
|
||||
}
|
||||
if !current.active {
|
||||
current.active = true;
|
||||
current.screen.on_active(renderer, ui_container);
|
||||
}
|
||||
current.screen.tick(delta, renderer, ui_container)
|
||||
};
|
||||
// Handle current
|
||||
current.screen.tick(delta, renderer, ui_container);
|
||||
if let Some(swap) = swap {
|
||||
self.replace_screen(swap);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_scroll(&mut self, x: f64, y: f64) {
|
||||
|
@ -149,15 +153,19 @@ pub fn button_action<F: Fn(&mut ::Game, &mut ui::Container) + 'static>(ui_contai
|
|||
click: F) {
|
||||
let button = ui_container.get_mut(&btn);
|
||||
button.add_hover_func(move |over, _, ui_container| {
|
||||
let txt = txt.clone();
|
||||
if let Some(txt) = txt {
|
||||
let text = ui_container.get_mut(&txt);
|
||||
text.set_b(if over {
|
||||
160
|
||||
} else {
|
||||
255
|
||||
});
|
||||
}
|
||||
let disabled = {
|
||||
let button = ui_container.get_mut(&btn);
|
||||
button.is_disabled()
|
||||
};
|
||||
let txt = txt.clone();
|
||||
if let Some(txt) = txt {
|
||||
let text = ui_container.get_mut(&txt);
|
||||
text.set_b(if over && !disabled {
|
||||
160
|
||||
} else {
|
||||
255
|
||||
});
|
||||
}
|
||||
});
|
||||
button.add_click_func(click);
|
||||
}
|
||||
|
|
|
@ -459,7 +459,7 @@ impl super::Screen for ServerList {
|
|||
fn tick(&mut self,
|
||||
delta: f64,
|
||||
renderer: &mut render::Renderer,
|
||||
ui_container: &mut ui::Container) {
|
||||
ui_container: &mut ui::Container) -> Option<Box<super::Screen>> {
|
||||
if *self.needs_reload.borrow() {
|
||||
self.reload_server_list(renderer, ui_container);
|
||||
}
|
||||
|
@ -551,6 +551,7 @@ impl super::Screen for ServerList {
|
|||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn on_scroll(&mut self, _: f64, y: f64) {
|
||||
|
|
|
@ -119,8 +119,8 @@ impl TextBox {
|
|||
(self.width, self.height)
|
||||
}
|
||||
|
||||
pub fn get_input(&self) -> &str {
|
||||
&self.input
|
||||
pub fn get_input(&self) -> String {
|
||||
self.input.clone()
|
||||
}
|
||||
|
||||
pub fn set_input(&mut self, renderer: &render::Renderer, input: &str) {
|
||||
|
|
Loading…
Reference in New Issue