Initial commit
This commit is contained in:
commit
412bbba1ee
|
@ -0,0 +1,2 @@
|
|||
*.lock
|
||||
/target
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
|
||||
name = "steven_rust"
|
||||
version = "0.0.1"
|
||||
authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
|
||||
|
||||
[dependencies]
|
||||
rustc-serialize = "0.3"
|
||||
glfw = "0.1.0"
|
||||
byteorder = "0.3.13"
|
||||
|
||||
[dependencies.steven_gl]
|
||||
path = "./gl"
|
||||
version = "0"
|
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "steven_gl"
|
||||
version = "0.0.1"
|
||||
authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.0.27"
|
||||
khronos_api = "0.0.8"
|
||||
|
||||
[dependencies]
|
||||
gl_common = "0.0.4"
|
||||
libc = "*"
|
|
@ -0,0 +1,19 @@
|
|||
extern crate gl_generator;
|
||||
extern crate khronos_api;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::BufWriter;
|
||||
use std::path::Path;
|
||||
|
||||
fn main() {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let dest = Path::new(&out_dir);
|
||||
|
||||
let mut file = BufWriter::new(File::create(&dest.join("bindings.rs")).unwrap());
|
||||
gl_generator::generate_bindings(gl_generator::GlobalGenerator,
|
||||
gl_generator::registry::Ns::Gl,
|
||||
gl_generator::Fallbacks::All,
|
||||
khronos_api::GL_XML, vec![], "3.2", "core",
|
||||
&mut file).unwrap();
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
@ -0,0 +1,49 @@
|
|||
|
||||
pub struct Map {
|
||||
bits: Vec<u64>,
|
||||
bitSize: usize,
|
||||
length: usize
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map() {
|
||||
let mut map = Map::new(4096, 4);
|
||||
for i in 0 .. 4096 {
|
||||
for j in 0 .. 16 {
|
||||
map.set(i, j);
|
||||
if map.get(i) != j {
|
||||
panic!("Fail");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Map {
|
||||
pub fn new(len: usize, size: usize) -> Map {
|
||||
let mut map = Map {
|
||||
bitSize: size,
|
||||
length: len,
|
||||
bits: Vec::with_capacity((len*size)/64)
|
||||
};
|
||||
for _ in 0 .. len {
|
||||
map.bits.push(0)
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
pub fn set(&mut self, i: usize, val: usize) {
|
||||
let mut i = i * self.bitSize;
|
||||
let pos = i / 64;
|
||||
let mask = (1 << self.bitSize) - 1;
|
||||
i %= 64;
|
||||
self.bits[pos] = (self.bits[pos] & !(mask << i )) | ((val << i) as u64)
|
||||
}
|
||||
|
||||
pub fn get(&mut self, i: usize) -> usize {
|
||||
let mut i = i * self.bitSize;
|
||||
let pos = i / 64;
|
||||
let mask = (1 << self.bitSize) - 1;
|
||||
i %= 64;
|
||||
((self.bits[pos] >> i) & mask) as usize
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
pub mod set;
|
||||
pub mod map;
|
||||
|
||||
pub use self::set::Set;
|
||||
pub use self::map::Map;
|
|
@ -0,0 +1,43 @@
|
|||
|
||||
pub struct Set {
|
||||
data : Vec<u64>
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
let mut set = Set::new(200);
|
||||
for i in 0 .. 200 {
|
||||
if i % 3 == 0 {
|
||||
set.set(i, true)
|
||||
}
|
||||
}
|
||||
for i in 0 .. 200 {
|
||||
if set.get(i) != (i%3 == 0) {
|
||||
panic!("Fail")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Set {
|
||||
pub fn new(size: usize) -> Set {
|
||||
let mut set = Set {
|
||||
data: Vec::with_capacity(size)
|
||||
};
|
||||
for _ in 0 .. size {
|
||||
set.data.push(0)
|
||||
}
|
||||
set
|
||||
}
|
||||
|
||||
pub fn set(&mut self, i: usize, v: bool) {
|
||||
if v {
|
||||
self.data[i>>6] |= 1 << (i & 0x3F)
|
||||
} else {
|
||||
self.data[i>>6] &= !(1 << (i & 0x3F))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&mut self, i: usize) -> bool {
|
||||
return (self.data[i>>6] & (1 << (i & 0x3F))) != 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
extern crate steven_gl as gl;
|
||||
extern crate glfw;
|
||||
|
||||
use std::ops::BitOr;
|
||||
|
||||
pub fn init(window: &mut glfw::Window) {
|
||||
gl::load_with(|s| window.get_proc_address(s));
|
||||
}
|
||||
|
||||
pub fn clear_color(r: f32, g: f32, b: f32, a: f32) {
|
||||
unsafe { gl::ClearColor(r, g, b, a); }
|
||||
}
|
||||
|
||||
pub enum ClearFlags {
|
||||
Color,
|
||||
Depth,
|
||||
Internal(u32)
|
||||
}
|
||||
|
||||
impl ClearFlags {
|
||||
fn internal(self) -> u32 {
|
||||
match self {
|
||||
ClearFlags::Color => gl::COLOR_BUFFER_BIT,
|
||||
ClearFlags::Depth => gl::DEPTH_BUFFER_BIT,
|
||||
ClearFlags::Internal(val) => val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitOr for ClearFlags {
|
||||
type Output = ClearFlags;
|
||||
|
||||
fn bitor(self, rhs: ClearFlags) -> ClearFlags {
|
||||
ClearFlags::Internal(self.internal() | rhs.internal())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(flags: ClearFlags) {
|
||||
unsafe { gl::Clear(flags.internal()) }
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
extern crate glfw;
|
||||
extern crate byteorder;
|
||||
|
||||
pub mod bit;
|
||||
pub mod protocol;
|
||||
|
||||
mod gl;
|
||||
use glfw::{Action, Context, Key};
|
||||
|
||||
fn main() {
|
||||
let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();
|
||||
|
||||
glfw.window_hint(glfw::WindowHint::ContextVersion(3, 2));
|
||||
glfw.window_hint(glfw::WindowHint::OpenGlProfile(glfw::OpenGlProfileHint::Core));
|
||||
glfw.window_hint(glfw::WindowHint::OpenGlForwardCompat(true));
|
||||
glfw.window_hint(glfw::WindowHint::DepthBits(32));
|
||||
glfw.window_hint(glfw::WindowHint::StencilBits(0));
|
||||
|
||||
let (mut window, events) = glfw.create_window(854, 480, "Steven", glfw::WindowMode::Windowed)
|
||||
.expect("Failed to create GLFW window");
|
||||
|
||||
gl::init(&mut window);
|
||||
|
||||
window.set_key_polling(true);
|
||||
window.make_current();
|
||||
glfw.set_swap_interval(1);
|
||||
|
||||
while !window.should_close() {
|
||||
gl::clear_color(1.0, 0.0, 0.0, 1.0);
|
||||
gl::clear(gl::ClearFlags::Color | gl::ClearFlags::Depth);
|
||||
|
||||
window.swap_buffers();
|
||||
glfw.poll_events();
|
||||
for (_, event) in glfw::flush_messages(&events) {
|
||||
handle_window_event(&mut window, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_window_event(window: &mut glfw::Window, event: glfw::WindowEvent) {
|
||||
match event {
|
||||
glfw::WindowEvent::Key(Key::Escape, _, Action::Press, _) => {
|
||||
window.set_should_close(true)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
#![allow(dead_code)]
|
||||
extern crate byteorder;
|
||||
|
||||
use std::net::TcpStream;
|
||||
use std::io;
|
||||
use std::io::{Write, Read};
|
||||
use std::convert;
|
||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||
|
||||
/// Serializes types into a buffer
|
||||
macro_rules! serialize_type {
|
||||
($dst:expr, $name:expr, u16) => {
|
||||
$dst.write_u16::<BigEndian>($name).unwrap();
|
||||
};
|
||||
($dst:expr, $name:expr, i64) => {
|
||||
$dst.write_i64::<BigEndian>($name).unwrap();
|
||||
};
|
||||
($dst:expr, $name:expr, VarInt) => {
|
||||
try!(write_varint($dst, $name));
|
||||
};
|
||||
($dst:expr, $name:expr, String) => {
|
||||
try!(write_varint($dst, $name.len() as i32));
|
||||
$dst.extend($name.bytes());
|
||||
};
|
||||
($dst:expr, $name:expr, Empty) => {
|
||||
|
||||
};
|
||||
($dst:expr, $name:expr, $ftype:ident) => {
|
||||
unimplemented!()
|
||||
};
|
||||
}
|
||||
|
||||
/// Deserializes types from a buffer
|
||||
macro_rules! deserialize_type {
|
||||
($src:expr, String) => {
|
||||
{
|
||||
let len = read_variant(&mut $src).unwrap();
|
||||
let mut ret = String::new();
|
||||
(&mut $src).take(len as u64).read_to_string(&mut ret).unwrap();
|
||||
ret
|
||||
}
|
||||
};
|
||||
($src:expr, i64) => {
|
||||
$src.read_i64::<BigEndian>().unwrap()
|
||||
};
|
||||
($src:expr, Empty) => { Empty };
|
||||
($src:expr, $ftype:ident) => {
|
||||
unimplemented!()
|
||||
};
|
||||
}
|
||||
|
||||
/// Helper macro for defining packets
|
||||
#[macro_export]
|
||||
macro_rules! state_packets {
|
||||
($($state:ident $stateName:ident {
|
||||
$($dir:ident $dirName:ident {
|
||||
$($name:ident => $id:expr {
|
||||
$($field:ident: $field_type:ident),+
|
||||
}),*
|
||||
})+
|
||||
})+) => {
|
||||
use protocol::*;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||
|
||||
pub enum Packet {
|
||||
$(
|
||||
$(
|
||||
$(
|
||||
$name($state::$dir::$name),
|
||||
)*
|
||||
)+
|
||||
)+
|
||||
}
|
||||
|
||||
$(
|
||||
pub mod $state {
|
||||
$(
|
||||
pub mod $dir {
|
||||
use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
|
||||
use protocol::*;
|
||||
use std::io;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
$(
|
||||
pub struct $name {
|
||||
$(pub $field: $field_type),+,
|
||||
}
|
||||
|
||||
impl PacketType for $name {
|
||||
|
||||
fn packet_id(&self) -> i32{ $id }
|
||||
|
||||
fn write(self, buf: &mut Vec<u8>) -> Result<(), io::Error> {
|
||||
$(
|
||||
serialize_type!(buf, self.$field, $field_type);
|
||||
)+
|
||||
|
||||
Result::Ok(())
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
)+
|
||||
}
|
||||
)+
|
||||
|
||||
/// Returns the packet for the given state, direction and id after parsing the fields
|
||||
/// from the buffer.
|
||||
pub fn packet_by_id(state: State, dir: Direction, id: i32, mut buf: &mut io::Cursor<Vec<u8>>) -> Option<Packet> {
|
||||
match state {
|
||||
$(
|
||||
State::$stateName => {
|
||||
match dir {
|
||||
$(
|
||||
Direction::$dirName => {
|
||||
match id {
|
||||
$(
|
||||
$id => Option::Some(Packet::$name($state::$dir::$name {
|
||||
$($field: deserialize_type!(buf, $field_type)),+,
|
||||
})),
|
||||
)*
|
||||
_ => Option::None
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod packet;
|
||||
|
||||
/// VarInt have a variable size (between 1 and 5 bytes) when encoded based
|
||||
/// on the size of the number
|
||||
pub type VarInt = i32;
|
||||
|
||||
/// Encodes a VarInt into the Writer
|
||||
pub fn write_varint(buf: &mut io::Write, v: VarInt) -> Result<(), io::Error> {
|
||||
const PART : u32 = 0x7F;
|
||||
let mut val = v as u32;
|
||||
loop {
|
||||
if (val & !PART) == 0 {
|
||||
try!(buf.write_u8(val as u8));
|
||||
return Result::Ok(());
|
||||
}
|
||||
try!(buf.write_u8(((val & PART) | 0x80) as u8));
|
||||
val >>= 7;
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes a VarInt from the Reader
|
||||
pub fn read_variant(buf: &mut io::Read) -> Result<VarInt, io::Error> {
|
||||
const PART : u32 = 0x7F;
|
||||
let mut size = 0;
|
||||
let mut val = 0u32;
|
||||
loop {
|
||||
let b = try!(buf.read_u8()) as u32;
|
||||
val |= (b & PART) << (size * 7);
|
||||
size+=1;
|
||||
if size > 5 {
|
||||
return Result::Err(io::Error::new(io::ErrorKind::InvalidInput, Error::Err("VarInt too big".to_string())))
|
||||
}
|
||||
if (b & 0x80) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
Result::Ok(val as VarInt)
|
||||
}
|
||||
|
||||
/// Direction is used to define whether packets are going to the
|
||||
/// server or the client.
|
||||
pub enum Direction {
|
||||
Serverbound,
|
||||
Clientbound
|
||||
}
|
||||
|
||||
/// The protocol has multiple 'sub-protocols' or states which control which
|
||||
/// packet an id points to.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum State {
|
||||
Handshaking,
|
||||
Play,
|
||||
Status,
|
||||
Login
|
||||
}
|
||||
|
||||
/// Return for any protocol related error.
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Err(String),
|
||||
IOError(io::Error),
|
||||
}
|
||||
|
||||
impl convert::From<io::Error> for Error {
|
||||
fn from(e : io::Error) -> Error {
|
||||
Error::IOError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match self {
|
||||
&Error::Err(ref val) => &val[..],
|
||||
&Error::IOError(ref e) => e.description(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Error {
|
||||
fn fmt(&self, f : &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
match self {
|
||||
&Error::Err(ref val) => write!(f, "protocol error: {}", val),
|
||||
&Error::IOError(ref e) => e.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Helper for empty structs
|
||||
pub struct Empty;
|
||||
|
||||
pub struct Conn {
|
||||
stream: TcpStream,
|
||||
host: String,
|
||||
port: u16,
|
||||
direction: Direction,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl Conn {
|
||||
pub fn new(target: &str) -> Result<Conn, Error>{
|
||||
// TODO SRV record support
|
||||
let stream = match TcpStream::connect(target) {
|
||||
Ok(val) => val,
|
||||
Err(err) => return Result::Err(Error::IOError(err))
|
||||
};
|
||||
let parts = target.split(":").collect::<Vec<&str>>();
|
||||
Result::Ok(Conn {
|
||||
stream: stream,
|
||||
host: parts[0].to_owned(),
|
||||
port: parts[1].parse().unwrap(),
|
||||
direction: Direction::Serverbound,
|
||||
state: State::Handshaking,
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: compression and encryption
|
||||
|
||||
pub fn write_packet<T: PacketType>(&mut self, packet: T) -> Result<(), Error> {
|
||||
let mut buf = Vec::new();
|
||||
try!(write_varint(&mut buf, packet.packet_id()));
|
||||
try!(packet.write(&mut buf));
|
||||
try!(write_varint(&mut self.stream, buf.len() as i32));
|
||||
try!(self.stream.write_all(&buf.into_boxed_slice()));
|
||||
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
pub fn read_packet(&mut self) -> Result<packet::Packet, Error> {
|
||||
let len = try!(read_variant(&mut self.stream)) as usize;
|
||||
let mut ibuf = Vec::with_capacity(len);
|
||||
try!((&mut self.stream).take(len as u64).read_to_end(&mut ibuf));
|
||||
|
||||
let mut buf = io::Cursor::new(ibuf);
|
||||
let id = try!(read_variant(&mut buf));
|
||||
|
||||
let dir = match self.direction {
|
||||
Direction::Clientbound => Direction::Serverbound,
|
||||
Direction::Serverbound => Direction::Clientbound,
|
||||
};
|
||||
|
||||
let packet = packet::packet_by_id(self.state, dir, id, &mut buf);
|
||||
|
||||
match packet {
|
||||
Some(val) => {
|
||||
let pos = buf.position() as usize;
|
||||
let ibuf = buf.into_inner();
|
||||
if ibuf.len() < pos {
|
||||
return Result::Err(Error::Err(format!("Failed to read all of packet 0x{:X}, had {} bytes left", id, ibuf.len() - pos)))
|
||||
}
|
||||
Result::Ok(val)
|
||||
},
|
||||
None => Result::Err(Error::Err("missing packet".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PacketType {
|
||||
fn packet_id(&self) -> i32;
|
||||
|
||||
fn write(self, buf: &mut Vec<u8>) -> Result<(), io::Error>;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut c = Conn::new("localhost:25565").unwrap();
|
||||
|
||||
c.write_packet(packet::handshake::serverbound::Handshake{
|
||||
protocol_version: 69,
|
||||
host: "localhost".to_string(),
|
||||
port: 25565,
|
||||
next: 1,
|
||||
}).unwrap();
|
||||
c.state = State::Status;
|
||||
c.write_packet(packet::status::serverbound::StatusRequest{empty: Empty}).unwrap();
|
||||
|
||||
match c.read_packet().unwrap() {
|
||||
packet::Packet::StatusResponse(val) => println!("{}", val.status),
|
||||
_ => panic!("Wrong packet"),
|
||||
}
|
||||
|
||||
c.write_packet(packet::status::serverbound::StatusPing{ping: 4433}).unwrap();
|
||||
|
||||
match c.read_packet().unwrap() {
|
||||
packet::Packet::StatusPong(val) => println!("{}", val.ping),
|
||||
_ => panic!("Wrong packet"),
|
||||
}
|
||||
|
||||
panic!("TODO!");
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
state_packets!(
|
||||
handshake Handshaking {
|
||||
serverbound Serverbound {
|
||||
// Handshake is the first packet sent in the protocol.
|
||||
// Its used for deciding if the request is a client
|
||||
// is requesting status information about the server
|
||||
// (MOTD, players etc) or trying to login to the server.
|
||||
//
|
||||
// The host and port fields are not used by the vanilla
|
||||
// server but are there for virtual server hosting to
|
||||
// be able to redirect a client to a target server with
|
||||
// a single address + port.
|
||||
//
|
||||
// Some modified servers/proxies use the handshake field
|
||||
// differently, packing information into the field other
|
||||
// than the hostname due to the protocol not providing
|
||||
// any system for custom information to be transfered
|
||||
// by the client to the server until after login.
|
||||
Handshake => 0 {
|
||||
// The protocol version of the connecting client
|
||||
protocol_version: VarInt,
|
||||
// The hostname the client connected to
|
||||
host: String,
|
||||
// The port the client connected to
|
||||
port: u16,
|
||||
// The next protocol state the client wants
|
||||
next: VarInt
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {
|
||||
}
|
||||
}
|
||||
play Play {
|
||||
serverbound Serverbound {
|
||||
}
|
||||
clientbound Clientbound {
|
||||
}
|
||||
}
|
||||
login Login {
|
||||
serverbound Serverbound {
|
||||
}
|
||||
clientbound Clientbound {
|
||||
}
|
||||
}
|
||||
status Status {
|
||||
serverbound Serverbound {
|
||||
// StatusRequest is sent by the client instantly after
|
||||
// switching to the Status protocol state and is used
|
||||
// to signal the server to send a StatusResponse to the
|
||||
// client
|
||||
StatusRequest => 0 {
|
||||
empty: Empty
|
||||
},
|
||||
// StatusPing is sent by the client after recieving a
|
||||
// StatusResponse. The client uses the time from sending
|
||||
// the ping until the time of recieving a pong to measure
|
||||
// the latency between the client and the server.
|
||||
StatusPing => 1 {
|
||||
ping: i64
|
||||
}
|
||||
}
|
||||
clientbound Clientbound {
|
||||
// StatusResponse is sent as a reply to a StatusRequest.
|
||||
// The Status should contain a json encoded structure with
|
||||
// version information, a player sample, a description/MOTD
|
||||
// and optionally a favicon.
|
||||
//
|
||||
// The structure is as follows
|
||||
// {
|
||||
// "version": {
|
||||
// "name": "1.8.3",
|
||||
// "protocol": 47,
|
||||
// },
|
||||
// "players": {
|
||||
// "max": 20,
|
||||
// "online": 1,
|
||||
// "sample": [
|
||||
// {"name": "Thinkofdeath", "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"}
|
||||
// ]
|
||||
// },
|
||||
// "description": "Hello world",
|
||||
// "favicon": "data:image/png;base64,<data>"
|
||||
// }
|
||||
StatusResponse => 0 {
|
||||
status: String
|
||||
},
|
||||
// StatusPong is sent as a reply to a StatusPing.
|
||||
// The Time field should be exactly the same as the
|
||||
// one sent by the client.
|
||||
StatusPong => 1 {
|
||||
ping: i64
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
Loading…
Reference in New Issue