Merge branch 'master' into 1.13_assets

This commit is contained in:
ice_iix 2021-01-06 18:12:19 -08:00
commit 8f614e2f28
61 changed files with 3777 additions and 3354 deletions

View File

@ -17,7 +17,7 @@ jobs:
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: 1.44.1
toolchain: 1.49.0
components: clippy, rustfmt
default: true
- name: Install dependencies

2910
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -18,37 +18,37 @@ path = "src/main.rs"
opt-level = 1
[dependencies]
cfg-if = "0.1.9"
wasm-bindgen = "0.2.64"
glutin = "0.22.0"
cfg-if = "1.0.0"
wasm-bindgen = "0.2.69"
winit = { version = "0.24.0", features = [ "web-sys" ]}
glow = "0.7.1"
byteorder = "1.3.4"
serde = "1.0.114"
serde_json = "1.0.56"
flate2 = { version = "1.0.16", features = ["rust_backend"], default-features = false }
serde = "1.0.118"
serde_json = "1.0.61"
flate2 = { version = "1.0.19", features = ["rust_backend"], default-features = false }
zip = { version = "0.5.6", features = ["deflate"], default-features = false }
image = "0.23.6"
rand = "0.7.3"
rand_pcg = "0.2.1"
base64 = "0.12.3"
log = { version = "0.4.8", features = ["std"] }
image = "0.23.12"
getrandom = { version = "0.2", features = ["js"] }
rand = "0.8.0"
rand_pcg = "0.3.0"
base64 = "0.13.0"
log = { version = "0.4.11", features = ["std"] }
cgmath = "0.17.0"
lazy_static = "1.4.0"
collision = "0.20.1"
rsa_public_encrypt_pkcs1 = "0.2.0"
structopt = "0.3.15"
rsa_public_encrypt_pkcs1 = "0.3.0"
structopt = "0.3.21"
clipboard = "0.5.0"
instant = "0.1.9"
# clippy = "*"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
reqwest = { version = "0.10.6", features = [ "blocking" ]}
reqwest = { version = "0.10.10", features = [ "blocking" ]}
glutin = "0.26.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
stdweb = "0.4.20"
winit = { version = "0.20.0", features = [ "stdweb" ]}
[dependencies.steven_gl]
path = "./gl"
version = "0"
console_error_panic_hook = "0.1.6"
web-sys = "0.3.46"
[dependencies.steven_resources]
path = "./resources"

View File

@ -1,7 +1,7 @@
# Stevenarella
[![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Ficeiix%2Fstevenarella%2Fbadge%3Fref%3Dmaster&style=plastic)](https://actions-badge.atrox.dev/iceiix/stevenarella/goto?ref=master)
Multi-protocol Minecraft-compatible client written in Rust
Multi-protocol Minecraft-compatible client written in Rust.
Don't expect it to go anywhere, just doing this for fun.
@ -13,16 +13,20 @@ Don't expect it to go anywhere, just doing this for fun.
In action: http://gfycat.com/NeedyElaborateGypsymoth
## Community chatroom
## Community
We have a chatroom on [EsperNet](https://esper.net): `irc.esper.net` server, `#stevenarella` channel.
IRC channels: `#stevenarella` on [irc.freenode.net](https://freenode.net), or `#stevenarella` on [irc.esper.net](https://esper.net).
Discussion forum: [https://github.com/iceiix/stevenarella/discussions](https://github.com/iceiix/stevenarella/discussions).
Join with your favorite IRC client.
## Protocol support
| Game version | Protocol version | Supported? |
| ------ | --- | --- |
| 1.16.4 | 754 | ✓ |
| 1.16.3 | 753 | ✓ |
| 1.16.2 | 751 | ✓ |
| 1.16.1 | 736 | ✓ |
| 1.16 | 735 | ✓ |
| 1.15.2 | 578 | ✓ |
@ -60,9 +64,23 @@ Windows, Ubuntu Linux, and macOS users can download pre-compiled builds
from [GitHub Actions](https://actions-badge.atrox.dev/iceiix/stevenarella/goto?ref=master).
(Click the artifacts drop-down and select your platform.)
## Building
## Dependencies
Requires Rust stable version 1.44.1 or newer to build.
Requires Rust stable version 1.49.0 or newer.
**Debian/Ubuntu**
```bash
sudo apt-get install libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-composite0-dev
```
**Alpine Linux**
```bash
sudo apk add openssl-dev xcb-util-dev
```
## Building
Compile and run:
```bash

View File

@ -2245,10 +2245,7 @@ define_blocks! {
model { ("minecraft", "iron_bars") },
collision pane_collision(north, south, east, west),
update_state (world, pos) => {
let f = |block| match block {
Block::IronBars{..} => true,
_ => false,
};
let f = |block| matches!(block, Block::IronBars{..});
let (north, south, west, east) = can_connect_sides(world, pos, &f);
Block::IronBars{north, south, west, east, waterlogged}
@ -2574,16 +2571,13 @@ define_blocks! {
model { ("minecraft", "nether_brick_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let f = |block| match block {
Block::NetherBrickFence{..} |
let f = |block| matches!(block, Block::NetherBrickFence{..} |
Block::FenceGate{..} |
Block::SpruceFenceGate{..} |
Block::BirchFenceGate{..} |
Block::JungleFenceGate{..} |
Block::DarkOakFenceGate{..} |
Block::AcaciaFenceGate{..} => true,
_ => false,
};
Block::AcaciaFenceGate{..});
let (north, south, west, east) = can_connect_sides(world, pos, &f);
Block::NetherBrickFence{north, south, west, east, waterlogged}
@ -3075,22 +3069,17 @@ define_blocks! {
material material::NON_SOLID,
model { ("minecraft", format!("{}_wall", variant.as_string())) },
update_state (world, pos) => {
let f = |block| match block {
Block::CobblestoneWall{..} |
let f = |block| matches!(block, Block::CobblestoneWall{..} |
Block::FenceGate{..} |
Block::SpruceFenceGate{..} |
Block::BirchFenceGate{..} |
Block::JungleFenceGate{..} |
Block::DarkOakFenceGate{..} |
Block::AcaciaFenceGate{..} => true,
_ => false,
};
Block::AcaciaFenceGate{..});
let (north, south, west, east) = can_connect_sides(world, pos, &f);
let up = !(match world.get_block(pos.shift(Direction::Up)) {
Block::Air{..} => true,
_ => false,
}) || !((north && south && !west && !east) || (!north && !south && west && east));
let up = !(matches!(world.get_block(pos.shift(Direction::Up)), Block::Air{..}))
|| !((north && south && !west && !east) || (!north && !south && west && east));
Block::CobblestoneWall{up, north, south, west, east, variant, waterlogged}
},
multipart (key, val) => match key {
@ -4686,12 +4675,12 @@ define_blocks! {
collision
},
update_state (world, pos) => Block::ChorusPlant {
up: match world.get_block(pos.shift(Direction::Up)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} => true, _ => false,},
down: match world.get_block(pos.shift(Direction::Down)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} | Block::EndStone{..} => true, _ => false,},
north: match world.get_block(pos.shift(Direction::North)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} => true, _ => false,},
south: match world.get_block(pos.shift(Direction::South)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} => true, _ => false,},
west: match world.get_block(pos.shift(Direction::West)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} => true, _ => false,},
east: match world.get_block(pos.shift(Direction::East)) { Block::ChorusPlant{..} | Block::ChorusFlower{..} => true, _ => false,},
up: matches!(world.get_block(pos.shift(Direction::Up)), Block::ChorusPlant{..} | Block::ChorusFlower{..}),
down: matches!(world.get_block(pos.shift(Direction::Down)), Block::ChorusPlant{..} | Block::ChorusFlower{..} | Block::EndStone{..}),
north: matches!(world.get_block(pos.shift(Direction::North)), Block::ChorusPlant{..} | Block::ChorusFlower{..}),
south: matches!(world.get_block(pos.shift(Direction::South)), Block::ChorusPlant{..} | Block::ChorusFlower{..}),
west: matches!(world.get_block(pos.shift(Direction::West)), Block::ChorusPlant{..} | Block::ChorusFlower{..}),
east: matches!(world.get_block(pos.shift(Direction::East)), Block::ChorusPlant{..} | Block::ChorusFlower{..}),
},
multipart (key, val) => match key {
"up" => up == (val == "true"),
@ -5626,8 +5615,7 @@ define_blocks! {
}
fn can_burn<W: WorldAccess>(world: &W, pos: Position) -> bool {
match world.get_block(pos) {
Block::Planks { .. }
matches!(world.get_block(pos), Block::Planks { .. }
| Block::DoubleWoodenSlab { .. }
| Block::WoodenSlab { .. }
| Block::FenceGate { .. }
@ -5663,16 +5651,11 @@ fn can_burn<W: WorldAccess>(world: &W, pos: Position) -> bool {
| Block::Vine { .. }
| Block::CoalBlock { .. }
| Block::HayBlock { .. }
| Block::Carpet { .. } => true,
_ => false,
}
| Block::Carpet { .. })
}
fn is_snowy<W: WorldAccess>(world: &W, pos: Position) -> bool {
match world.get_block(pos.shift(Direction::Up)) {
Block::Snow { .. } | Block::SnowLayer { .. } => true,
_ => false,
}
matches!(world.get_block(pos.shift(Direction::Up)), Block::Snow { .. } | Block::SnowLayer { .. })
}
fn can_connect_sides<F: Fn(Block) -> bool, W: WorldAccess>(
@ -5694,8 +5677,7 @@ fn can_connect<F: Fn(Block) -> bool, W: WorldAccess>(world: &W, pos: Position, f
}
fn can_connect_fence(block: Block) -> bool {
match block {
Block::Fence { .. }
matches!(block, Block::Fence { .. }
| Block::SpruceFence { .. }
| Block::BirchFence { .. }
| Block::JungleFence { .. }
@ -5706,19 +5688,14 @@ fn can_connect_fence(block: Block) -> bool {
| Block::BirchFenceGate { .. }
| Block::JungleFenceGate { .. }
| Block::DarkOakFenceGate { .. }
| Block::AcaciaFenceGate { .. } => true,
_ => false,
}
| Block::AcaciaFenceGate { .. })
}
fn can_connect_glasspane(block: Block) -> bool {
match block {
Block::Glass { .. }
matches!(block, Block::Glass { .. }
| Block::StainedGlass { .. }
| Block::GlassPane { .. }
| Block::StainedGlassPane { .. } => true,
_ => false,
}
| Block::StainedGlassPane { .. })
}
fn can_connect_redstone<W: WorldAccess>(world: &W, pos: Position, dir: Direction) -> RedstoneSide {
@ -5729,11 +5706,7 @@ fn can_connect_redstone<W: WorldAccess>(world: &W, pos: Position, dir: Direction
let side_up = world.get_block(shift_pos.shift(Direction::Up));
let up = world.get_block(pos.shift(Direction::Up));
if match side_up {
Block::RedstoneWire { .. } => true,
_ => false,
} && !up.get_material().should_cull_against
{
if matches!(side_up, Block::RedstoneWire { .. }) && !up.get_material().should_cull_against {
return RedstoneSide::Up;
}
@ -5741,13 +5714,9 @@ fn can_connect_redstone<W: WorldAccess>(world: &W, pos: Position, dir: Direction
}
let side_down = world.get_block(shift_pos.shift(Direction::Down));
if match block {
Block::RedstoneWire { .. } => true,
_ => false,
} || match side_down {
Block::RedstoneWire { .. } => true,
_ => false,
} {
if matches!(block, Block::RedstoneWire { .. })
|| matches!(side_down, Block::RedstoneWire { .. })
{
return RedstoneSide::Side;
}
RedstoneSide::None
@ -5961,10 +5930,7 @@ fn door_collision(facing: Direction, hinge: Side, open: bool) -> Vec<Aabb3<f64>>
}
fn update_repeater_state<W: WorldAccess>(world: &W, pos: Position, facing: Direction) -> bool {
let f = |dir| match world.get_block(pos.shift(dir)) {
Block::RepeaterPowered { .. } => true,
_ => false,
};
let f = |dir| matches!(world.get_block(pos.shift(dir)), Block::RepeaterPowered { .. });
f(facing.clockwise()) || f(facing.counter_clockwise())
}

56
gl/Cargo.lock generated
View File

@ -1,56 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "gl_generator"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "khronos_api"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "steven_gl"
version = "0.0.1"
dependencies = [
"gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
"khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "xml-rs"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
"checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
"checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5"

View File

@ -1,13 +0,0 @@
[package]
name = "steven_gl"
version = "0.0.1"
authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>" ]
edition = "2018"
build = "build.rs"
[build-dependencies]
gl_generator = "0.14.0"
khronos_api = "3.1.0"
[dependencies]
libc = "0.2.71"

View File

@ -1,15 +0,0 @@
use gl_generator::{Api, Fallbacks, GlobalGenerator, Profile, Registry};
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());
Registry::new(Api::Gl, (3, 2), Profile::Core, Fallbacks::All, [])
.write_bindings(GlobalGenerator, &mut file)
.unwrap();
}

View File

@ -1,4 +0,0 @@
#![allow(clippy::unused_unit)]
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::too_many_arguments)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

1329
protocol/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -5,16 +5,17 @@ authors = [ "Thinkofdeath <thinkofdeath@spigotmc.org>", "iceiix <ice_ix@protonma
edition = "2018"
[dependencies]
serde = "1.0.114"
serde_json = "1.0.56"
serde = "1.0.118"
serde_json = "1.0.60"
hex = "0.4.2"
sha-1 = "0.9.1"
aes = "0.4.0"
cfb8 = "0.4.0"
byteorder = "1.3.4"
log = { version = "0.4.8", features = ["std"] }
flate2 = { version = "1.0.16", features = ["rust_backend"], default-features = false }
log = { version = "0.4.11", features = ["std"] }
flate2 = { version = "1.0.19", features = ["rust_backend"], default-features = false }
num-traits = "0.2.12"
instant = "0.1.9"
[dependencies.steven_shared]
path = "../shared"
@ -25,4 +26,4 @@ path = "../std_or_web"
version = "0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
reqwest = { version = "0.10.6", features = [ "blocking" ]}
reqwest = { version = "0.10.10", features = [ "blocking" ]}

View File

@ -323,8 +323,8 @@ pub fn convert_legacy(c: &mut Component) {
current.text = txt.text[last..i].to_owned();
last = next.0 + 1;
let mut modifier = if (color_char >= 'a' && color_char <= 'f')
|| (color_char >= '0' && color_char <= '9')
let mut modifier = if ('a'..='f').contains(&color_char)
|| ('0'..='9').contains(&color_char)
{
Default::default()
} else {

View File

@ -72,10 +72,7 @@ impl Tag {
}
pub fn is_compound(&self) -> bool {
match *self {
Tag::Compound(_) => true,
_ => false,
}
matches!(*self, Tag::Compound(_))
}
pub fn as_byte(&self) -> Option<i8> {

View File

@ -18,7 +18,6 @@
use aes::Aes128;
use cfb8::stream_cipher::{NewStreamCipher, StreamCipher};
use cfb8::Cfb8;
#[cfg(not(target_arch = "wasm32"))]
use std_or_web::fs;
pub mod forge;
@ -30,6 +29,7 @@ use crate::shared::Position;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use flate2::read::{ZlibDecoder, ZlibEncoder};
use flate2::Compression;
use instant::{Duration, Instant};
use log::debug;
use std::convert;
use std::default;
@ -38,11 +38,10 @@ use std::io;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::time::{Duration, Instant};
pub const SUPPORTED_PROTOCOLS: [i32; 21] = [
736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210, 109, 107, 74,
47, 5,
pub const SUPPORTED_PROTOCOLS: [i32; 24] = [
754, 753, 751, 736, 735, 578, 575, 498, 490, 485, 480, 477, 452, 451, 404, 340, 316, 315, 210,
109, 107, 74, 47, 5,
];
static CURRENT_PROTOCOL_VERSION: AtomicI32 = AtomicI32::new(SUPPORTED_PROTOCOLS[0]);
@ -119,7 +118,7 @@ macro_rules! state_packets {
packet::versions::translate_internal_packet_id_for_version(version, State::$stateName, Direction::$dirName, internal_ids::$name, false)
}
fn write<W: io::Write>(self, buf: &mut W) -> Result<(), Error> {
fn write<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
$(
if true $(&& ($cond(&self)))* {
self.$field.write_to(buf)?;
@ -1156,6 +1155,8 @@ impl Conn {
let pos = buf.position() as usize;
let ibuf = buf.into_inner();
if ibuf.len() != pos {
debug!("pos = {:?}", pos);
debug!("ibuf = {:?}", ibuf);
return Result::Err(Error::Err(format!(
"Failed to read all of packet 0x{:X}, \
had {} bytes left",
@ -1306,6 +1307,44 @@ impl Conn {
}
}
/// Parse a clientbound packet, for debugging packet parsing issues (Conn::read_packet)
pub fn try_parse_packet(ibuf: Vec<u8>, protocol_version: i32) {
println!("trying to parse packet data {:?}", ibuf);
let mut buf = io::Cursor::new(ibuf);
let id = VarInt::read_from(&mut buf).unwrap().0;
let dir = Direction::Clientbound;
let state = State::Play; // TODO: allow parsing other states
println!(
"about to parse id={:x}, dir={:?} state={:?}",
id, dir, state
);
let packet = packet::packet_by_id(protocol_version, state, dir, id, &mut buf).unwrap();
println!("packet = {:?}", packet);
match packet {
Some(_val) => {
let pos = buf.position() as usize;
let ibuf = buf.into_inner();
if ibuf.len() != pos {
println!("pos = {:?}", pos);
println!("ibuf = {:?}", ibuf);
println!(
"Failed to read all of packet 0x{:X}, \
had {} bytes left",
id,
ibuf.len() - pos
)
}
}
None => println!("missing packet"),
}
}
#[derive(Debug)]
pub struct Status {
pub version: StatusVersion,
@ -1387,5 +1426,5 @@ impl Clone for Conn {
pub trait PacketType {
fn packet_id(&self, protocol_version: i32) -> i32;
fn write<W: io::Write>(self, buf: &mut W) -> Result<(), Error>;
fn write<W: io::Write>(&self, buf: &mut W) -> Result<(), Error>;
}

View File

@ -353,6 +353,16 @@ state_packets!(
field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1),
field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1),
}
/// SetDisplayedRecipe replaces CraftingBookData, type 0.
packet SetDisplayedRecipe {
field recipe_id: String =,
}
/// SetRecipeBookState replaces CraftingBookData, type 1.
packet SetRecipeBookState {
field book_id: VarInt =, // TODO: enum, 0: crafting, 1: furnace, 2: blast furnace, 3: smoker
field book_open: bool =,
field filter_active: bool =,
}
packet NameItem {
field item_name: String =,
}
@ -881,6 +891,11 @@ state_packets!(
field message: format::Component =,
}
/// MultiBlockChange is used to update a batch of blocks in a single packet.
packet MultiBlockChange_Packed {
field chunk_section_pos: u64 =,
field no_trust_edges: bool =,
field records: LenPrefixed<VarInt, VarLong> =,
}
packet MultiBlockChange_VarInt {
field chunk_x: i32 =,
field chunk_z: i32 =,
@ -1047,6 +1062,16 @@ state_packets!(
}
/// ChunkData sends or updates a single chunk on the client. If New is set
/// then biome data should be sent too.
packet ChunkData_Biomes3D_VarInt {
field chunk_x: i32 =,
field chunk_z: i32 =,
field new: bool =,
field bitmask: VarInt =,
field heightmaps: Option<nbt::NamedTag> =,
field biomes: LenPrefixed<VarInt, VarInt> = when(|p: &ChunkData_Biomes3D_VarInt| p.new),
field data: LenPrefixedBytes<VarInt> =,
field block_entities: LenPrefixed<VarInt, Option<nbt::NamedTag>> =,
}
packet ChunkData_Biomes3D_bool {
field chunk_x: i32 =,
field chunk_z: i32 =,
@ -1148,12 +1173,12 @@ state_packets!(
field offset_z: f32 =,
field speed: f32 =,
field count: i32 =,
field block_state: VarInt = when(|p: &Particle_f64| p.particle_id == 3 || p.particle_id == 20),
field red: f32 = when(|p: &Particle_f64| p.particle_id == 11),
field green: f32 = when(|p: &Particle_f64| p.particle_id == 11),
field blue: f32 = when(|p: &Particle_f64| p.particle_id == 11),
field scale: f32 = when(|p: &Particle_f64| p.particle_id == 11),
field item: Option<nbt::NamedTag> = when(|p: &Particle_f64| p.particle_id == 27),
field block_state: VarInt = when(|p: &Particle_f64| p.particle_id == 3 || p.particle_id == 23),
field red: f32 = when(|p: &Particle_f64| p.particle_id == 14),
field green: f32 = when(|p: &Particle_f64| p.particle_id == 14),
field blue: f32 = when(|p: &Particle_f64| p.particle_id == 14),
field scale: f32 = when(|p: &Particle_f64| p.particle_id == 14),
field item: Option<nbt::NamedTag> = when(|p: &Particle_f64| p.particle_id == 32),
}
packet Particle_Data {
field particle_id: i32 =,
@ -1166,12 +1191,30 @@ state_packets!(
field offset_z: f32 =,
field speed: f32 =,
field count: i32 =,
field block_state: VarInt = when(|p: &Particle_Data| p.particle_id == 3 || p.particle_id == 20),
field red: f32 = when(|p: &Particle_Data| p.particle_id == 11),
field green: f32 = when(|p: &Particle_Data| p.particle_id == 11),
field blue: f32 = when(|p: &Particle_Data| p.particle_id == 11),
field scale: f32 = when(|p: &Particle_Data| p.particle_id == 11),
field item: Option<nbt::NamedTag> = when(|p: &Particle_Data| p.particle_id == 27),
field block_state: VarInt = when(|p: &Particle_Data| p.particle_id == 3 || p.particle_id == 23),
field red: f32 = when(|p: &Particle_Data| p.particle_id == 14),
field green: f32 = when(|p: &Particle_Data| p.particle_id == 14),
field blue: f32 = when(|p: &Particle_Data| p.particle_id == 14),
field scale: f32 = when(|p: &Particle_Data| p.particle_id == 14),
field item: Option<nbt::NamedTag> = when(|p: &Particle_Data| p.particle_id == 32),
}
packet Particle_Data13 {
field particle_id: i32 =,
field long_distance: bool =,
field x: f32 =,
field y: f32 =,
field z: f32 =,
field offset_x: f32 =,
field offset_y: f32 =,
field offset_z: f32 =,
field speed: f32 =,
field count: i32 =,
field block_state: VarInt = when(|p: &Particle_Data13| p.particle_id == 3 || p.particle_id == 20),
field red: f32 = when(|p: &Particle_Data13| p.particle_id == 11),
field green: f32 = when(|p: &Particle_Data13| p.particle_id == 11),
field blue: f32 = when(|p: &Particle_Data13| p.particle_id == 11),
field scale: f32 = when(|p: &Particle_Data13| p.particle_id == 11),
field item: Option<nbt::NamedTag> = when(|p: &Particle_Data13| p.particle_id == 27),
}
packet Particle_VarIntArray {
field particle_id: i32 =,
@ -1200,6 +1243,39 @@ state_packets!(
}
/// JoinGame is sent after completing the login process. This
/// sets the initial state for the client.
packet JoinGame_WorldNames_IsHard {
/// The entity id the client will be referenced by
field entity_id: i32 =,
/// Whether hardcore mode is enabled
field is_hardcore: bool =,
/// The starting gamemode of the client
field gamemode: u8 =,
/// The previous gamemode of the client
field previous_gamemode: u8 =,
/// Identifiers for all worlds on the server
field world_names: LenPrefixed<VarInt, String> =,
/// Represents a dimension registry
field dimension_codec: Option<nbt::NamedTag> =,
/// The dimension the client is starting in
field dimension: Option<nbt::NamedTag> =,
/// The world being spawned into
field world_name: String =,
/// Truncated SHA-256 hash of world's seed
field hashed_seed: i64 =,
/// The max number of players on the server
field max_players: VarInt =,
/// The render distance (2-32)
field view_distance: VarInt =,
/// Whether the client should reduce the amount of debug
/// information it displays in F3 mode
field reduced_debug_info: bool =,
/// Whether to prompt or immediately respawn
field enable_respawn_screen: bool =,
/// Whether the world is in debug mode
field is_debug: bool =,
/// Whether the world is a superflat world
field is_flat: bool =,
}
packet JoinGame_WorldNames {
/// The entity id the client will be referenced by
field entity_id: i32 =,
@ -1231,7 +1307,6 @@ state_packets!(
/// Whether the world is a superflat world
field is_flat: bool =,
}
packet JoinGame_HashedSeed_Respawn {
/// The entity id the client will be referenced by
field entity_id: i32 =,
@ -1317,6 +1392,7 @@ state_packets!(
field item_damage: VarInt =,
field scale: i8 =,
field tracking_position: bool =,
field locked: bool =,
field icons: LenPrefixed<VarInt, packet::MapIcon> =,
field columns: u8 =,
field rows: Option<u8> = when(|p: &Maps| p.columns > 0),
@ -1324,6 +1400,17 @@ state_packets!(
field z: Option<u8> = when(|p: &Maps| p.columns > 0),
field data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps| p.columns > 0),
}
packet Maps_NoLocked {
field item_damage: VarInt =,
field scale: i8 =,
field tracking_position: bool =,
field icons: LenPrefixed<VarInt, packet::MapIcon> =,
field columns: u8 =,
field rows: Option<u8> = when(|p: &Maps_NoLocked| p.columns > 0),
field x: Option<u8> = when(|p: &Maps_NoLocked| p.columns > 0),
field z: Option<u8> = when(|p: &Maps_NoLocked| p.columns > 0),
field data: Option<LenPrefixedBytes<VarInt>> = when(|p: &Maps_NoLocked| p.columns > 0),
}
packet Maps_NoTracking {
field item_damage: VarInt =,
field scale: i8 =,
@ -1527,6 +1614,19 @@ state_packets!(
field recipe_ids: LenPrefixed<VarInt, String> =,
field recipe_ids2: LenPrefixed<VarInt, String> = when(|p: &UnlockRecipes_WithSmelting| p.action.0 == 0),
}
packet UnlockRecipes_WithBlastSmoker {
field action: VarInt =,
field crafting_book_open: bool =,
field filtering_craftable: bool =,
field smelting_book_open: bool =,
field filtering_smeltable: bool =,
field blast_furnace_open: bool =,
field filtering_blast_furnace: bool =,
field smoker_open: bool =,
field filtering_smoker: bool =,
field recipe_ids: LenPrefixed<VarInt, String> =,
field recipe_ids2: LenPrefixed<VarInt, String> = when(|p: &UnlockRecipes_WithBlastSmoker| p.action.0 == 0),
}
/// EntityDestroy destroys the entities with the ids in the provided slice.
packet EntityDestroy {
field entity_ids: LenPrefixed<VarInt, VarInt> =,
@ -1564,6 +1664,16 @@ state_packets!(
field gamemode: u8 =,
field level_type: String =,
}
packet Respawn_NBT {
field dimension: Option<nbt::NamedTag> =,
field world_name: String =,
field hashed_seed: i64 =,
field gamemode: u8 =,
field previous_gamemode: u8 =,
field is_debug: bool =,
field is_flat: bool =,
field copy_metadata: bool =,
}
packet Respawn_WorldName {
field dimension: String =,
field world_name: String =,
@ -1668,6 +1778,10 @@ state_packets!(
/// EntityEquipment is sent to display an item on an entity, like a sword
/// or armor. Slot 0 is the held item and slots 1 to 4 are boots, leggings
/// chestplate and helmet respectively.
packet EntityEquipment_Array {
field entity_id: VarInt =,
field equipments: packet::EntityEquipments =,
}
packet EntityEquipment_VarInt {
field entity_id: VarInt =,
field slot: VarInt =,
@ -2435,6 +2549,57 @@ impl Serializable for CriterionProgress {
}
}
#[derive(Debug, Default)]
pub struct EntityEquipment {
pub slot: u8,
pub item: Option<item::Stack>,
}
impl Serializable for EntityEquipment {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
Ok(EntityEquipment {
slot: Serializable::read_from(buf)?,
item: Serializable::read_from(buf)?,
})
}
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
self.slot.write_to(buf)?;
self.item.write_to(buf)
}
}
// Top-bit terminated array of EntityEquipment
#[derive(Debug, Default)]
pub struct EntityEquipments {
pub equipments: Vec<EntityEquipment>,
}
impl Serializable for EntityEquipments {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
let mut equipments: Vec<EntityEquipment> = vec![];
loop {
let e: EntityEquipment = Serializable::read_from(buf)?;
equipments.push(EntityEquipment {
slot: e.slot & 0x7f,
item: e.item,
});
if e.slot & 0x80 == 0 {
break;
}
// TODO: detect infinite loop
}
Ok(EntityEquipments { equipments })
}
fn write_to<W: io::Write>(&self, _buf: &mut W) -> Result<(), Error> {
unimplemented!()
}
}
#[derive(Debug, Default)]
pub struct EntityProperty {
pub key: String,

View File

@ -14,6 +14,7 @@ mod v1_14_3;
mod v1_14_4;
mod v1_15;
mod v1_16_1;
mod v1_16_4;
mod v1_7_10;
mod v1_8_9;
mod v1_9;
@ -25,6 +26,9 @@ mod v1_9_2;
pub fn protocol_name_to_protocol_version(s: String) -> i32 {
match s.as_ref() {
"" => SUPPORTED_PROTOCOLS[0],
"1.16.4" => 754,
"1.16.3" => 753,
"1.16.2" => 751,
"1.16.1" => 736,
"1.16" => 735,
"1.15.2" => 578,
@ -64,6 +68,7 @@ pub fn translate_internal_packet_id_for_version(
to_internal: bool,
) -> i32 {
match version {
754 | 753 | 751 => v1_16_4::translate_internal_packet_id(state, dir, id, to_internal),
736 => v1_16_1::translate_internal_packet_id(state, dir, id, to_internal),
735 => v1_16_1::translate_internal_packet_id(state, dir, id, to_internal),
578 => v1_15::translate_internal_packet_id(state, dir, id, to_internal),

View File

@ -74,7 +74,7 @@ protocol_packet_ids!(
0x22 => Particle_VarIntArray
0x23 => NamedSoundEffect_u8_NoCategory
0x24 => JoinGame_i8
0x25 => Maps
0x25 => Maps_NoLocked
0x26 => EntityMove_i8
0x27 => EntityLookAndMove_i8
0x28 => EntityLook_VarInt

View File

@ -91,7 +91,7 @@ protocol_packet_ids!(
0x23 => Effect
0x24 => Particle_VarIntArray
0x25 => JoinGame_i32
0x26 => Maps
0x26 => Maps_NoLocked
0x27 => Entity
0x28 => EntityMove_i16
0x29 => EntityLookAndMove_i16

View File

@ -89,7 +89,7 @@ protocol_packet_ids!(
0x21 => KeepAliveClientbound_i64
0x22 => ChunkData_HeightMap
0x23 => Effect
0x24 => Particle_Data
0x24 => Particle_Data13
0x25 => JoinGame_i32
0x26 => Maps
0x27 => Entity

View File

@ -76,7 +76,7 @@ protocol_packet_ids!(
0x21 => Effect
0x22 => Particle_VarIntArray
0x23 => JoinGame_i32
0x24 => Maps
0x24 => Maps_NoLocked
0x25 => EntityMove_i16
0x26 => EntityLookAndMove_i16
0x27 => EntityLook_VarInt

View File

@ -76,7 +76,7 @@ protocol_packet_ids!(
0x21 => Effect
0x22 => Particle_VarIntArray
0x23 => JoinGame_i32
0x24 => Maps
0x24 => Maps_NoLocked
0x25 => EntityMove_i16
0x26 => EntityLookAndMove_i16
0x27 => EntityLook_VarInt

View File

@ -79,7 +79,7 @@ protocol_packet_ids!(
0x21 => Effect
0x22 => Particle_VarIntArray
0x23 => JoinGame_i32
0x24 => Maps
0x24 => Maps_NoLocked
0x25 => Entity
0x26 => EntityMove_i16
0x27 => EntityLookAndMove_i16

View File

@ -89,9 +89,9 @@ protocol_packet_ids!(
0x21 => KeepAliveClientbound_i64
0x22 => ChunkData
0x23 => Effect
0x24 => Particle_Data
0x24 => Particle_Data13
0x25 => JoinGame_i32
0x26 => Maps
0x26 => Maps_NoLocked
0x27 => Entity
0x28 => EntityMove_i16
0x29 => EntityLookAndMove_i16

View File

@ -128,7 +128,7 @@ protocol_packet_ids!(
0x44 => EntityMetadata
0x45 => EntityAttach
0x46 => EntityVelocity
0x47 => EntityEquipment_VarInt // TODO: changed to an array, but earlier than 1.16.1
0x47 => EntityEquipment_Array
0x48 => SetExperience
0x49 => UpdateHealth
0x4a => ScoreboardObjective

View File

@ -0,0 +1,179 @@
protocol_packet_ids!(
handshake Handshaking {
serverbound Serverbound {
0x00 => Handshake
}
clientbound Clientbound {
}
}
play Play {
serverbound Serverbound {
0x00 => TeleportConfirm
0x01 => QueryBlockNBT
0x02 => SetDifficulty
0x03 => ChatMessage
0x04 => ClientStatus
0x05 => ClientSettings
0x06 => TabComplete
0x07 => ConfirmTransactionServerbound
0x08 => ClickWindowButton
0x09 => ClickWindow
0x0a => CloseWindow
0x0b => PluginMessageServerbound
0x0c => EditBook
0x0d => QueryEntityNBT
0x0e => UseEntity_Sneakflag
0x0f => GenerateStructure
0x10 => KeepAliveServerbound_i64
0x11 => LockDifficulty
0x12 => PlayerPosition
0x13 => PlayerPositionLook
0x14 => PlayerLook
0x15 => Player
0x16 => VehicleMove
0x17 => SteerBoat
0x18 => PickItem
0x19 => CraftRecipeRequest
0x1a => ClientAbilities_u8
0x1b => PlayerDigging
0x1c => PlayerAction
0x1d => SteerVehicle
0x1e => SetDisplayedRecipe
0x1f => SetRecipeBookState
0x20 => NameItem
0x21 => ResourcePackStatus
0x22 => AdvancementTab
0x23 => SelectTrade
0x24 => SetBeaconEffect
0x25 => HeldItemChange
0x26 => UpdateCommandBlock
0x27 => UpdateCommandBlockMinecart
0x28 => CreativeInventoryAction
0x29 => UpdateJigsawBlock_Joint
0x2a => UpdateStructureBlock
0x2b => SetSign
0x2c => ArmSwing
0x2d => SpectateTeleport
0x2e => PlayerBlockPlacement_insideblock
0x2f => UseItem
}
clientbound Clientbound {
0x00 => SpawnObject_VarInt
0x01 => SpawnExperienceOrb
0x02 => SpawnMob_NoMeta
0x03 => SpawnPainting_VarInt
0x04 => SpawnPlayer_f64_NoMeta
0x05 => Animation
0x06 => Statistics
0x07 => AcknowledgePlayerDigging
0x08 => BlockBreakAnimation
0x09 => UpdateBlockEntity
0x0a => BlockAction
0x0b => BlockChange_VarInt
0x0c => BossBar
0x0d => ServerDifficulty_Locked
0x0e => ServerMessage_Sender
0x0f => TabCompleteReply
0x10 => DeclareCommands
0x11 => ConfirmTransaction
0x12 => WindowClose
0x13 => WindowItems
0x14 => WindowProperty
0x15 => WindowSetSlot
0x16 => SetCooldown
0x17 => PluginMessageClientbound
0x18 => NamedSoundEffect
0x19 => Disconnect
0x1a => EntityAction
0x1b => Explosion
0x1c => ChunkUnload
0x1d => ChangeGameState
0x1e => WindowOpenHorse
0x1f => KeepAliveClientbound_i64
0x20 => ChunkData_Biomes3D_VarInt
0x21 => Effect
0x22 => Particle_f64
0x23 => UpdateLight_WithTrust
0x24 => JoinGame_WorldNames_IsHard
0x25 => Maps
0x26 => TradeList_WithRestock
0x27 => EntityMove_i16
0x28 => EntityLookAndMove_i16
0x29 => EntityLook_VarInt
0x2a => Entity
0x2b => VehicleTeleport
0x2c => OpenBook
0x2d => WindowOpen_VarInt
0x2e => SignEditorOpen
0x2f => CraftRecipeResponse
0x30 => PlayerAbilities
0x31 => CombatEvent
0x32 => PlayerInfo
0x33 => FacePlayer
0x34 => TeleportPlayer_WithConfirm
0x35 => UnlockRecipes_WithBlastSmoker
0x36 => EntityDestroy
0x37 => EntityRemoveEffect
0x38 => ResourcePackSend
0x39 => Respawn_NBT
0x3a => EntityHeadLook
0x3b => MultiBlockChange_Packed
0x3c => SelectAdvancementTab
0x3d => WorldBorder
0x3e => Camera
0x3f => SetCurrentHotbarSlot
0x40 => UpdateViewPosition
0x41 => UpdateViewDistance
0x42 => SpawnPosition
0x43 => ScoreboardDisplay
0x44 => EntityMetadata
0x45 => EntityAttach
0x46 => EntityVelocity
0x47 => EntityEquipment_Array
0x48 => SetExperience
0x49 => UpdateHealth
0x4a => ScoreboardObjective
0x4b => SetPassengers
0x4c => Teams_VarInt
0x4d => UpdateScore
0x4e => TimeUpdate
0x4f => Title
0x50 => EntitySoundEffect
0x51 => SoundEffect
0x52 => StopSound
0x53 => PlayerListHeaderFooter
0x54 => NBTQueryResponse
0x55 => CollectItem
0x56 => EntityTeleport_f64
0x57 => Advancements
0x58 => EntityProperties
0x59 => EntityEffect
0x5a => DeclareRecipes
0x5b => TagsWithEntities
}
}
login Login {
serverbound Serverbound {
0x00 => LoginStart
0x01 => EncryptionResponse
0x02 => LoginPluginResponse
}
clientbound Clientbound {
0x00 => LoginDisconnect
0x01 => EncryptionRequest
0x02 => LoginSuccess_UUID
0x03 => SetInitialCompression
0x04 => LoginPluginRequest
}
}
status Status {
serverbound Serverbound {
0x00 => StatusRequest
0x01 => StatusPing
}
clientbound Clientbound {
0x00 => StatusResponse
0x01 => StatusPong
}
}
);

View File

@ -76,7 +76,7 @@ protocol_packet_ids!(
0x21 => Effect
0x22 => Particle_VarIntArray
0x23 => JoinGame_i8
0x24 => Maps
0x24 => Maps_NoLocked
0x25 => EntityMove_i16
0x26 => EntityLookAndMove_i16
0x27 => EntityLook_VarInt

View File

@ -76,7 +76,7 @@ protocol_packet_ids!(
0x21 => Effect
0x22 => Particle_VarIntArray
0x23 => JoinGame_i32
0x24 => Maps
0x24 => Maps_NoLocked
0x25 => EntityMove_i16
0x26 => EntityLookAndMove_i16
0x27 => EntityLook_VarInt

View File

@ -16,6 +16,7 @@ pub struct Map {
bits: Vec<u64>,
pub bit_size: usize,
length: usize,
padded: bool,
}
#[test]
@ -53,17 +54,20 @@ impl Map {
bit_size: size,
length: len,
bits: Vec::with_capacity((len * size) / 64),
padded: false,
};
for _ in 0..len {
map.bits.push(0)
}
map
}
pub fn from_raw(bits: Vec<u64>, size: usize) -> Map {
pub fn from_raw(bits: Vec<u64>, size: usize, padded: bool) -> Map {
Map {
length: (bits.len() * 64 + (size - 1)) / size,
bit_size: size,
bits,
padded,
}
}
@ -75,8 +79,17 @@ impl Map {
n
}
fn get_bit_offset(&self, i: usize) -> usize {
let padding = if self.padded {
i / (64 / self.bit_size) * (64 % self.bit_size)
} else {
0
};
i * self.bit_size + padding
}
pub fn set(&mut self, i: usize, val: usize) {
let i = i * self.bit_size;
let i = self.get_bit_offset(i);
let pos = i / 64;
let mask = (1u64 << self.bit_size) - 1;
let ii = i % 64;
@ -90,7 +103,7 @@ impl Map {
}
pub fn get(&self, i: usize) -> usize {
let i = i * self.bit_size;
let i = self.get_bit_offset(i);
let pos = i / 64;
let mask = (1 << self.bit_size) - 1;
let ii = i % 64;

View File

@ -39,23 +39,14 @@ impl Gamemode {
}
pub fn can_fly(&self) -> bool {
match *self {
Gamemode::Creative | Gamemode::Spectator => true,
_ => false,
}
matches!(*self, Gamemode::Creative | Gamemode::Spectator)
}
pub fn always_fly(&self) -> bool {
match *self {
Gamemode::Spectator => true,
_ => false,
}
matches!(*self, Gamemode::Spectator)
}
pub fn noclip(&self) -> bool {
match *self {
Gamemode::Spectator => true,
_ => false,
}
matches!(*self, Gamemode::Spectator)
}
}

View File

@ -10,8 +10,13 @@ use std::sync::mpsc;
use std::sync::{Arc, RwLock};
use std::thread;
#[cfg(not(target_arch = "wasm32"))]
const NUM_WORKERS: usize = 8;
// TODO: threads or web workers on wasm
#[cfg(target_arch = "wasm32")]
const NUM_WORKERS: usize = 0;
pub struct ChunkBuilder {
threads: Vec<(mpsc::Sender<BuildReq>, thread::JoinHandle<()>)>,
free_builders: Vec<(usize, Vec<u8>, Vec<u8>)>,
@ -302,7 +307,11 @@ fn flood_fill(snapshot: &world::Snapshot, visited: &mut Set, x: i32, y: i32, z:
let mut touched = 0;
while let Some((x, y, z)) = next_position.pop_front() {
let idx = (x | (z << 4) | (y << 8)) as usize;
if x < 0 || x > 15 || y < 0 || y > 15 || z < 0 || z > 15 || visited.get(idx) {
if !(0..=15).contains(&x)
|| !(0..=15).contains(&y)
|| !(0..=15).contains(&z)
|| visited.get(idx)
{
continue;
}
visited.set(idx, true);

View File

@ -12,9 +12,9 @@ use crate::types::Gamemode;
use crate::world;
use cgmath::{self, Decomposed, Matrix4, Point3, Quaternion, Rad, Rotation3, Vector3};
use collision::{Aabb, Aabb3};
use instant::Instant;
use std::collections::HashMap;
use std::hash::BuildHasherDefault;
use std::time::Instant;
pub fn add_systems(m: &mut ecs::Manager) {
let sys = MovementHandler::new(m);

View File

@ -151,7 +151,7 @@ impl ecs::System for LerpPosition {
pos.position = pos.position
+ (target_pos.position - pos.position) * delta * target_pos.lerp_amount;
let len = (pos.position - target_pos.position).magnitude2();
if len < 0.001 || len > 100.0 * 100.0 {
if !(0.001..=100.0 * 100.0).contains(&len) {
pos.position = target_pos.position;
}
}

View File

@ -12,18 +12,24 @@
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate steven_gl as gl;
use log::{error, info};
use std::ffi;
use glow as gl;
use glow::{HasContext, PixelPackData, PixelUnpackData};
use log::error;
use std::mem;
use std::ops::BitOr;
use std::ops::{Deref, DerefMut};
use std::ptr;
static mut CONTEXT: *mut glow::Context = 0 as *mut glow::Context;
/// Inits the gl library. This should be called once a context is ready.
pub fn init(vid: &glutin::WindowedContext<glutin::PossiblyCurrent>) {
gl::load_with(|s| vid.get_proc_address(s) as *const _);
pub fn init(context: glow::Context) {
unsafe {
CONTEXT = Box::into_raw(Box::new(context));
}
}
fn glow_context() -> &'static glow::Context {
unsafe { CONTEXT.as_ref().unwrap() }
}
/// Dsed to specify how the vertices will be handled
@ -42,32 +48,20 @@ pub const POINTS: DrawType = gl::POINTS;
pub fn draw_arrays(ty: DrawType, offset: usize, count: usize) {
unsafe {
gl::DrawArrays(ty, offset as i32, count as i32);
glow_context().draw_arrays(ty, offset as i32, count as i32);
}
}
pub fn draw_elements(ty: DrawType, count: i32, dty: Type, offset: usize) {
unsafe {
gl::DrawElements(ty, count, dty, offset as *const gl::types::GLvoid);
glow_context().draw_elements(ty, count, dty, offset as i32);
}
}
pub fn multi_draw_elements(ty: DrawType, count: &[i32], dty: Type, offsets: &[usize]) {
unsafe {
gl::MultiDrawElements(
ty,
count.as_ptr(),
dty,
offsets.as_ptr() as *const _,
count.len() as i32,
);
}
}
/// Sets the size of the viewport of this context.
// Sets the size of the viewport of this context.
pub fn viewport(x: i32, y: i32, w: i32, h: i32) {
unsafe {
gl::Viewport(x, y, w, h);
glow_context().viewport(x, y, w, h);
}
}
@ -75,7 +69,7 @@ pub fn viewport(x: i32, y: i32, w: i32, h: i32) {
/// when Clear is called with the color flag.
pub fn clear_color(r: f32, g: f32, b: f32, a: f32) {
unsafe {
gl::ClearColor(r, g, b, a);
glow_context().clear_color(r, g, b, a);
}
}
@ -109,12 +103,12 @@ impl BitOr for ClearFlags {
/// Clears the buffers specified by the passed flags.
pub fn clear(flags: ClearFlags) {
unsafe { gl::Clear(flags.internal()) }
unsafe { glow_context().clear(flags.internal()) }
}
pub fn depth_mask(f: bool) {
unsafe {
gl::DepthMask(f as u8);
glow_context().depth_mask(f);
}
}
@ -130,7 +124,7 @@ pub const EQUAL: Func = gl::EQUAL;
pub fn depth_func(f: Func) {
unsafe {
gl::DepthFunc(f);
glow_context().depth_func(f);
}
}
@ -146,14 +140,14 @@ pub const MULTISAMPLE: Flag = gl::MULTISAMPLE;
/// Enables the passed flag.
pub fn enable(f: Flag) {
unsafe {
gl::Enable(f);
glow_context().enable(f);
}
}
/// Disables the passed flag.
pub fn disable(f: Flag) {
unsafe {
gl::Disable(f);
glow_context().disable(f);
}
}
@ -161,7 +155,7 @@ pub fn disable(f: Flag) {
/// currently active one.
pub fn active_texture(id: u32) {
unsafe {
gl::ActiveTexture(gl::TEXTURE0 + id);
glow_context().active_texture(gl::TEXTURE0 + id);
}
}
@ -175,7 +169,7 @@ pub const ZERO_FACTOR: Factor = gl::ZERO;
/// Sets the factors to be used when blending.
pub fn blend_func(s_factor: Factor, d_factor: Factor) {
unsafe {
gl::BlendFunc(s_factor, d_factor);
glow_context().blend_func(s_factor, d_factor);
}
}
@ -186,7 +180,7 @@ pub fn blend_func_separate(
d_factor_a: Factor,
) {
unsafe {
gl::BlendFuncSeparate(s_factor_rgb, d_factor_rgb, s_factor_a, d_factor_a);
glow_context().blend_func_separate(s_factor_rgb, d_factor_rgb, s_factor_a, d_factor_a);
}
}
@ -198,7 +192,7 @@ pub const FRONT: Face = gl::FRONT;
/// Sets the face to be culled by the gpu.
pub fn cull_face(face: Face) {
unsafe {
gl::CullFace(face);
glow_context().cull_face(face);
}
}
@ -211,7 +205,7 @@ pub const COUNTER_CLOCK_WISE: FaceDirection = gl::CCW;
/// Sets the direction of vertices used to specify the
/// front face (e.g. for culling).
pub fn front_face(dir: FaceDirection) {
unsafe { gl::FrontFace(dir) }
unsafe { glow_context().front_face(dir) }
}
/// `Type` is a type of data used by various operations.
@ -219,6 +213,7 @@ pub type Type = u32;
pub const UNSIGNED_BYTE: Type = gl::UNSIGNED_BYTE;
pub const UNSIGNED_SHORT: Type = gl::UNSIGNED_SHORT;
pub const UNSIGNED_INT: Type = gl::UNSIGNED_INT;
pub const BYTE: Type = gl::BYTE;
pub const SHORT: Type = gl::SHORT;
pub const FLOAT: Type = gl::FLOAT;
@ -265,22 +260,22 @@ pub const CLAMP_TO_EDGE: TextureValue = gl::CLAMP_TO_EDGE as TextureValue;
/// `Texture` is a buffer of data used by fragment shaders.
#[derive(Default)]
pub struct Texture(u32);
pub struct Texture(glow::Texture);
impl Texture {
// Allocates a new texture.
pub fn new() -> Texture {
let mut t = Texture(0);
unsafe {
gl::GenTextures(1, &mut t.0);
}
t
Texture(unsafe {
glow_context()
.create_texture()
.expect("create texture failed")
})
}
/// Binds the texture to the passed target.
pub fn bind(&self, target: TextureTarget) {
unsafe {
gl::BindTexture(target, self.0);
glow_context().bind_texture(target, Some(self.0));
}
}
@ -293,13 +288,7 @@ impl Texture {
pixels: &mut [u8],
) {
unsafe {
gl::GetTexImage(
target,
level,
format,
ty,
pixels.as_mut_ptr() as *mut gl::types::GLvoid,
);
glow_context().get_tex_image(target, level, format, ty, PixelPackData::Slice(pixels));
}
}
@ -314,11 +303,7 @@ impl Texture {
pix: Option<&[u8]>,
) {
unsafe {
let ptr = match pix {
Some(val) => val.as_ptr() as *const gl::types::GLvoid,
None => ptr::null(),
};
gl::TexImage2D(
glow_context().tex_image_2d(
target,
level,
format as i32,
@ -327,7 +312,7 @@ impl Texture {
0,
format,
ty,
ptr,
pix,
);
}
}
@ -345,7 +330,7 @@ impl Texture {
pix: &[u8],
) {
unsafe {
gl::TexSubImage2D(
glow_context().tex_sub_image_2d(
target,
level,
x as i32,
@ -354,7 +339,7 @@ impl Texture {
height as i32,
format,
ty,
pix.as_ptr() as *const _,
PixelUnpackData::Slice(pix),
);
}
}
@ -371,11 +356,7 @@ impl Texture {
pix: Option<&[u8]>,
) {
unsafe {
let ptr = match pix {
Some(val) => val.as_ptr() as *const gl::types::GLvoid,
None => ptr::null(),
};
gl::TexImage2D(
glow_context().tex_image_2d(
target,
level,
internal_format as i32,
@ -384,40 +365,7 @@ impl Texture {
0,
format,
ty,
ptr,
);
}
}
pub fn image_2d_sample(
&self,
target: TextureTarget,
samples: i32,
width: u32,
height: u32,
format: TextureFormat,
fixed: bool,
) {
unsafe {
let result: &mut [i32] = &mut [0; 1];
gl::GetIntegerv(gl::MAX_SAMPLES, &mut result[0]);
let use_samples = if samples > result[0] {
info!(
"glTexImage2DMultisample: requested {} samples but GL_MAX_SAMPLES is {}",
samples, result[0]
);
result[0]
} else {
samples
};
gl::TexImage2DMultisample(
target,
use_samples,
format,
width as i32,
height as i32,
fixed as u8,
pix,
);
}
}
@ -434,7 +382,7 @@ impl Texture {
pix: &[u8],
) {
unsafe {
gl::TexImage3D(
glow_context().tex_image_3d(
target,
level,
format as i32,
@ -444,7 +392,7 @@ impl Texture {
0,
format,
ty,
pix.as_ptr() as *const gl::types::GLvoid,
Some(pix),
);
}
}
@ -464,7 +412,7 @@ impl Texture {
pix: &[u8],
) {
unsafe {
gl::TexSubImage3D(
glow_context().tex_sub_image_3d(
target,
level,
x as i32,
@ -475,7 +423,7 @@ impl Texture {
depth as i32,
format,
ty,
pix.as_ptr() as *const gl::types::GLvoid,
PixelUnpackData::Slice(pix),
);
}
}
@ -487,7 +435,7 @@ impl Texture {
value: TextureValue,
) {
unsafe {
gl::TexParameteri(target, param, value);
glow_context().tex_parameter_i32(target, param, value);
}
}
}
@ -495,7 +443,7 @@ impl Texture {
impl Drop for Texture {
fn drop(&mut self) {
unsafe {
gl::DeleteTextures(1, &self.0);
glow_context().delete_texture(self.0);
}
}
}
@ -512,35 +460,38 @@ pub const COMPILE_STATUS: ShaderParameter = gl::COMPILE_STATUS;
pub const INFO_LOG_LENGTH: ShaderParameter = gl::INFO_LOG_LENGTH;
#[derive(Default)]
pub struct Program(u32);
pub struct Program(glow::Program);
impl Program {
pub fn new() -> Program {
Program(unsafe { gl::CreateProgram() })
Program(unsafe {
glow_context()
.create_program()
.expect("program creation failed")
})
}
pub fn attach_shader(&self, shader: Shader) {
unsafe {
gl::AttachShader(self.0, shader.0);
glow_context().attach_shader(self.0, shader.0);
}
}
pub fn link(&self) {
unsafe {
gl::LinkProgram(self.0);
glow_context().link_program(self.0);
}
}
pub fn use_program(&self) {
unsafe {
gl::UseProgram(self.0);
glow_context().use_program(Some(self.0));
}
}
pub fn uniform_location(&self, name: &str) -> Option<Uniform> {
let c_name = ffi::CString::new(name).unwrap();
let u = unsafe { gl::GetUniformLocation(self.0, c_name.as_ptr()) };
if u != -1 {
let u = unsafe { glow_context().get_uniform_location(self.0, name) };
if let Some(u) = u {
Some(Uniform(u))
} else {
None
@ -548,12 +499,9 @@ impl Program {
}
pub fn attribute_location(&self, name: &str) -> Option<Attribute> {
let a = unsafe {
let name_c = ffi::CString::new(name).unwrap();
gl::GetAttribLocation(self.0, name_c.as_ptr())
};
if a != -1 {
Some(Attribute(a))
let a = unsafe { glow_context().get_attrib_location(self.0, name) };
if let Some(a) = a {
Some(Attribute(a as i32))
} else {
None
}
@ -563,107 +511,107 @@ impl Program {
impl Drop for Program {
fn drop(&mut self) {
unsafe {
gl::DeleteProgram(self.0);
glow_context().delete_program(self.0);
}
}
}
pub struct Shader(u32);
pub struct Shader(glow::Shader);
impl Shader {
pub fn new(ty: ShaderType) -> Shader {
Shader(unsafe { gl::CreateShader(ty) })
Shader(unsafe {
glow_context()
.create_shader(ty)
.expect("failed to create shader")
})
}
pub fn set_source(&self, src: &str) {
unsafe {
let src_c = ffi::CString::new(src).unwrap();
gl::ShaderSource(self.0, 1, &src_c.as_ptr(), ptr::null());
glow_context().shader_source(self.0, src);
}
}
pub fn compile(&self) {
unsafe {
gl::CompileShader(self.0);
glow_context().compile_shader(self.0);
}
}
pub fn get_parameter(&self, param: ShaderParameter) -> i32 {
let mut ret: i32 = 0;
unsafe {
gl::GetShaderiv(self.0, param, &mut ret);
}
ret
pub fn get_shader_compile_status(&self) -> bool {
unsafe { glow_context().get_shader_compile_status(self.0) }
}
pub fn get_info_log(&self) -> String {
let len = self.get_parameter(INFO_LOG_LENGTH);
let mut data = Vec::<u8>::with_capacity(len as usize);
unsafe {
data.set_len(len as usize);
gl::GetShaderInfoLog(self.0, len, ptr::null_mut(), data.as_mut_ptr() as *mut i8);
}
String::from_utf8(data).unwrap()
unsafe { glow_context().get_shader_info_log(self.0) }
}
}
#[derive(Clone, Copy)]
pub struct Uniform(i32);
#[derive(Clone)]
pub struct Uniform(glow::UniformLocation);
impl Uniform {
pub fn set_int(&self, val: i32) {
unsafe {
gl::Uniform1i(self.0, val);
glow_context().uniform_1_i32(Some(&self.0), val);
}
}
pub fn set_int3(&self, x: i32, y: i32, z: i32) {
unsafe {
gl::Uniform3i(self.0, x, y, z);
glow_context().uniform_3_i32(Some(&self.0), x, y, z);
}
}
pub fn set_float(&self, val: f32) {
unsafe {
gl::Uniform1f(self.0, val);
glow_context().uniform_1_f32(Some(&self.0), val);
}
}
pub fn set_float2(&self, x: f32, y: f32) {
unsafe {
gl::Uniform2f(self.0, x, y);
glow_context().uniform_2_f32(Some(&self.0), x, y);
}
}
pub fn set_float3(&self, x: f32, y: f32, z: f32) {
unsafe {
gl::Uniform3f(self.0, x, y, z);
glow_context().uniform_3_f32(Some(&self.0), x, y, z);
}
}
pub fn set_float4(&self, x: f32, y: f32, z: f32, w: f32) {
unsafe {
gl::Uniform4f(self.0, x, y, z, w);
glow_context().uniform_4_f32(Some(&self.0), x, y, z, w);
}
}
#[allow(clippy::missing_safety_doc)]
pub unsafe fn set_float_multi_raw(&self, data: *const f32, len: usize) {
gl::Uniform4fv(self.0, len as i32, data);
pub fn set_float_multi(&self, v: &[[f32; 4]]) {
unsafe {
glow_context()
.uniform_4_f32_slice(Some(&self.0), &*(v as *const [[f32; 4]] as *const [f32; 4]))
}
}
pub fn set_matrix4(&self, m: &::cgmath::Matrix4<f32>) {
use cgmath::Matrix;
unsafe {
gl::UniformMatrix4fv(self.0, 1, false as u8, m.as_ptr());
glow_context().uniform_matrix_4_f32_slice(
Some(&self.0),
false,
&*(m as *const cgmath::Matrix4<f32> as *const [f32; 4 * 4]),
);
}
}
pub fn set_matrix4_multi(&self, m: &[::cgmath::Matrix4<f32>]) {
unsafe {
gl::UniformMatrix4fv(self.0, m.len() as i32, false as u8, m.as_ptr() as *const _);
// TODO: Most likely isn't safe
glow_context().uniform_matrix_4_f32_slice(
Some(&self.0),
false,
std::slice::from_raw_parts(m.as_ptr() as *const _, m.len() * 4 * 4),
); // TODO: Most likely isn't safe
}
}
}
@ -674,38 +622,32 @@ pub struct Attribute(i32);
impl Attribute {
pub fn enable(&self) {
unsafe {
gl::EnableVertexAttribArray(self.0 as u32);
glow_context().enable_vertex_attrib_array(self.0 as u32);
}
}
pub fn disable(&self) {
unsafe {
gl::DisableVertexAttribArray(self.0 as u32);
glow_context().disable_vertex_attrib_array(self.0 as u32);
}
}
pub fn vertex_pointer(&self, size: i32, ty: Type, normalized: bool, stride: i32, offset: i32) {
unsafe {
gl::VertexAttribPointer(
glow_context().vertex_attrib_pointer_f32(
self.0 as u32,
size,
ty,
normalized as u8,
normalized,
stride,
offset as *const gl::types::GLvoid,
offset,
);
}
}
pub fn vertex_pointer_int(&self, size: i32, ty: Type, stride: i32, offset: i32) {
unsafe {
gl::VertexAttribIPointer(
self.0 as u32,
size,
ty,
stride,
offset as *const gl::types::GLvoid,
);
glow_context().vertex_attrib_pointer_i32(self.0 as u32, size, ty, stride, offset);
}
}
}
@ -714,16 +656,16 @@ impl Attribute {
// This includes buffers, the format of the buffers and enabled
// attributes.
#[derive(Default)]
pub struct VertexArray(u32);
pub struct VertexArray(glow::VertexArray);
impl VertexArray {
/// Allocates a new `VertexArray`.
pub fn new() -> VertexArray {
let mut va = VertexArray(0);
unsafe {
gl::GenVertexArrays(1, &mut va.0);
}
va
VertexArray(unsafe {
glow_context()
.create_vertex_array()
.expect("create vertex array failed")
})
}
/// Marks the `VertexArray` as the currently active one, this
@ -731,7 +673,7 @@ impl VertexArray {
/// this `VertexArray`.
pub fn bind(&self) {
unsafe {
gl::BindVertexArray(self.0);
glow_context().bind_vertex_array(Some(self.0));
}
}
}
@ -739,9 +681,9 @@ impl VertexArray {
impl Drop for VertexArray {
fn drop(&mut self) {
unsafe {
gl::DeleteVertexArrays(1, &self.0);
glow_context().delete_vertex_array(self.0);
}
self.0 = 0;
self.0 = glow::VertexArray::default();
}
}
@ -775,16 +717,16 @@ pub const WRITE_ONLY: Access = gl::WRITE_ONLY;
/// `Buffer` is a storage for vertex data.
#[derive(Default)]
pub struct Buffer(u32);
pub struct Buffer(glow::Buffer);
impl Buffer {
/// Allocates a new Buffer.
pub fn new() -> Buffer {
let mut b = Buffer(0);
unsafe {
gl::GenBuffers(1, &mut b.0);
}
b
Buffer(unsafe {
glow_context()
.create_buffer()
.expect("create buffer failed")
})
}
/// Makes the buffer the currently active one for the given target.
@ -792,24 +734,19 @@ impl Buffer {
/// (Data, Map etc).
pub fn bind(&self, target: BufferTarget) {
unsafe {
gl::BindBuffer(target, self.0);
glow_context().bind_buffer(target, Some(self.0));
}
}
pub fn set_data(&self, target: BufferTarget, data: &[u8], usage: BufferUsage) {
unsafe {
gl::BufferData(
target,
data.len() as isize,
data.as_ptr() as *const gl::types::GLvoid,
usage,
);
glow_context().buffer_data_u8_slice(target, data, usage);
}
}
pub fn re_set_data(&self, target: BufferTarget, data: &[u8]) {
unsafe {
gl::BufferSubData(target, 0, data.len() as isize, data.as_ptr() as *const _);
glow_context().buffer_sub_data_u8_slice(target, 0, data);
}
}
@ -823,7 +760,11 @@ impl Buffer {
pub fn map(&self, target: BufferTarget, access: Access, length: usize) -> MappedBuffer {
unsafe {
MappedBuffer {
inner: Vec::from_raw_parts(gl::MapBuffer(target, access) as *mut u8, 0, length),
inner: Vec::from_raw_parts(
glow_context().map_buffer_range(target, 0, length as i32, access) as *mut u8,
0,
length,
),
target,
}
}
@ -833,7 +774,7 @@ impl Buffer {
impl Drop for Buffer {
fn drop(&mut self) {
unsafe {
gl::DeleteBuffers(1, &self.0);
glow_context().delete_buffer(self.0);
}
}
}
@ -860,7 +801,7 @@ impl DerefMut for MappedBuffer {
impl Drop for MappedBuffer {
fn drop(&mut self) {
unsafe {
gl::UnmapBuffer(self.target);
glow_context().unmap_buffer(self.target);
}
mem::forget(mem::replace(&mut self.inner, Vec::new()));
}
@ -875,11 +816,11 @@ pub const COLOR_ATTACHMENT_2: Attachment = gl::COLOR_ATTACHMENT2;
pub const DEPTH_ATTACHMENT: Attachment = gl::DEPTH_ATTACHMENT;
#[derive(Default)]
pub struct Framebuffer(u32);
pub struct Framebuffer(glow::Framebuffer);
pub fn check_framebuffer_status() {
unsafe {
let status = gl::CheckFramebufferStatus(gl::FRAMEBUFFER);
let status = glow_context().check_framebuffer_status(gl::FRAMEBUFFER);
let s = match status {
gl::FRAMEBUFFER_UNDEFINED => "GL_FRAMEBUFFER_UNDEFINED",
gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
@ -909,7 +850,7 @@ pub fn check_framebuffer_status() {
pub fn check_gl_error() {
unsafe {
loop {
let err = gl::GetError();
let err = glow_context().get_error();
if err == gl::NO_ERROR {
break;
}
@ -921,28 +862,28 @@ pub fn check_gl_error() {
impl Framebuffer {
pub fn new() -> Framebuffer {
let mut fb = Framebuffer(0);
unsafe {
gl::GenFramebuffers(1, &mut fb.0);
}
fb
Framebuffer(unsafe {
glow_context()
.create_framebuffer()
.expect("create framebuffer failed")
})
}
pub fn bind(&self) {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, self.0);
glow_context().bind_framebuffer(gl::FRAMEBUFFER, Some(self.0));
}
}
pub fn bind_read(&self) {
unsafe {
gl::BindFramebuffer(gl::READ_FRAMEBUFFER, self.0);
glow_context().bind_framebuffer(gl::READ_FRAMEBUFFER, Some(self.0));
}
}
pub fn bind_draw(&self) {
unsafe {
gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, self.0);
glow_context().bind_framebuffer(gl::DRAW_FRAMEBUFFER, Some(self.0));
}
}
@ -954,7 +895,13 @@ impl Framebuffer {
level: i32,
) {
unsafe {
gl::FramebufferTexture2D(gl::FRAMEBUFFER, attachment, target, tex.0, level);
glow_context().framebuffer_texture_2d(
gl::FRAMEBUFFER,
attachment,
target,
Some(tex.0),
level,
);
}
}
}
@ -962,40 +909,37 @@ impl Framebuffer {
impl Drop for Framebuffer {
fn drop(&mut self) {
unsafe {
gl::DeleteFramebuffers(1, &self.0);
glow_context().delete_framebuffer(self.0);
}
}
}
pub fn unbind_framebuffer() {
unsafe {
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
glow_context().bind_framebuffer(gl::FRAMEBUFFER, None);
}
}
pub fn unbind_framebuffer_read() {
unsafe {
gl::BindFramebuffer(gl::READ_FRAMEBUFFER, 0);
glow_context().bind_framebuffer(gl::READ_FRAMEBUFFER, None);
}
}
pub fn unbind_framebuffer_draw() {
unsafe {
gl::BindFramebuffer(gl::DRAW_FRAMEBUFFER, 0);
glow_context().bind_framebuffer(gl::DRAW_FRAMEBUFFER, None);
}
}
pub fn draw_buffers(bufs: &[Attachment]) {
unsafe {
gl::DrawBuffers(bufs.len() as i32, bufs.as_ptr());
glow_context().draw_buffers(bufs);
}
}
pub fn bind_frag_data_location(p: &Program, cn: u32, name: &str) {
unsafe {
let name_c = ffi::CString::new(name).unwrap();
gl::BindFragDataLocation(p.0, cn, name_c.as_ptr());
}
unsafe { glow_context().bind_frag_data_location(p.0, cn, name) }
}
pub fn blit_framebuffer(
@ -1011,7 +955,7 @@ pub fn blit_framebuffer(
filter: TextureValue,
) {
unsafe {
gl::BlitFramebuffer(
glow_context().blit_framebuffer(
sx0,
sy0,
sx1,
@ -1026,17 +970,12 @@ pub fn blit_framebuffer(
}
}
pub fn read_buffer(a: Attachment) {
unsafe {
gl::ReadBuffer(a);
}
}
pub type TargetBuffer = u32;
pub const COLOR: TargetBuffer = gl::COLOR;
pub fn clear_buffer(buffer: TargetBuffer, draw_buffer: i32, values: &[f32]) {
pub fn clear_buffer(buffer: TargetBuffer, draw_buffer: u32, values: &mut [f32]) {
unsafe {
gl::ClearBufferfv(buffer, draw_buffer, values.as_ptr());
// TODO: why does glow have &mut on clear buffer values, why would it change the color?
glow_context().clear_buffer_f32_slice(buffer, draw_buffer, values);
}
}

View File

@ -17,8 +17,9 @@
#![allow(clippy::many_single_char_names)] // short variable names provide concise clarity
#![allow(clippy::float_cmp)] // float comparison used to check if changed
use instant::{Duration, Instant};
use log::{error, info, warn};
use std::time::{Duration, Instant};
use std::fs;
extern crate steven_shared as shared;
use structopt::StructOpt;
@ -46,6 +47,7 @@ pub mod world;
use crate::protocol::mojang;
use cfg_if::cfg_if;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::mpsc;
@ -193,6 +195,10 @@ struct Opt {
#[structopt(short = "n", long = "network-debug")]
network_debug: bool,
/// Parse a network packet from a file
#[structopt(short = "N", long = "network-parse-packet")]
network_parse_packet: Option<String>,
/// Protocol version to use in the autodetection ping
#[structopt(short = "p", long = "default-protocol-version")]
default_protocol_version: Option<String>,
@ -200,6 +206,7 @@ struct Opt {
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
use glow::HasRenderLoop;
extern crate console_error_panic_hook;
pub use console_error_panic_hook::set_once as set_panic_hook;
} else {
@ -209,7 +216,7 @@ cfg_if! {
}
cfg_if! {
if #[cfg(target_os = "unknown")] {
if #[cfg(target_arch = "wasm32")] {
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
@ -224,7 +231,6 @@ fn main2() {
let opt = Opt::from_args();
set_panic_hook();
std::env::set_var("RUST_BACKTRACE", "1");
let con = Arc::new(Mutex::new(console::Console::new()));
let proxy = console::ConsoleProxy::new(con.clone());
@ -234,7 +240,7 @@ fn main2() {
info!("Starting steven");
let (vars, vsync) = {
let (vars, mut vsync) = {
let mut vars = console::Vars::new();
vars.register(CL_BRAND);
auth::register_vars(&mut vars);
@ -248,35 +254,88 @@ fn main2() {
let (res, mut resui) = resources::Manager::new();
let resource_manager = Arc::new(RwLock::new(res));
let events_loop = glutin::event_loop::EventLoop::new();
let window_builder = glutin::window::WindowBuilder::new()
.with_title("Stevenarella")
.with_inner_size(glutin::dpi::LogicalSize::new(854.0, 480.0));
let window = glutin::ContextBuilder::new()
.with_stencil_buffer(0)
.with_depth_buffer(24)
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (2, 0),
})
.with_gl_profile(glutin::GlProfile::Core)
.with_vsync(vsync)
.build_windowed(window_builder, &events_loop)
.expect("Could not create glutin window.");
let events_loop = winit::event_loop::EventLoop::new();
let mut window = unsafe {
window
.make_current()
.expect("Could not set current context.")
let window_builder = winit::window::WindowBuilder::new()
.with_title("Stevenarella")
.with_inner_size(winit::dpi::LogicalSize::new(854.0, 480.0));
#[cfg(target_arch = "wasm32")]
let (context, shader_version, dpi_factor, winit_window, render_loop) = {
let winit_window = window_builder.build(&events_loop).unwrap();
let dpi_factor = winit_window.scale_factor();
use wasm_bindgen::JsCast;
use winit::platform::web::WindowExtWebSys;
let canvas = winit_window.canvas();
let html_window = web_sys::window().unwrap();
let document = html_window.document().unwrap();
let body = document.body().unwrap();
body.append_child(&canvas)
.expect("Append canvas to HTML body");
let canvas = canvas.dyn_into::<web_sys::HtmlCanvasElement>().unwrap();
let webgl2_context = canvas
.get_context("webgl2")
.expect("Failed to get WebGL2 context")
.expect("Failed to create WebGL2 context, is WebGL2 support enabled? (https://get.webgl.org/webgl2/)")
.dyn_into::<web_sys::WebGl2RenderingContext>()
.unwrap();
(
glow::Context::from_webgl2_context(webgl2_context),
"#version 300 es", // WebGL 2
dpi_factor,
winit_window,
glow::RenderLoop::from_request_animation_frame(),
)
};
gl::init(&window);
#[cfg(not(target_arch = "wasm32"))]
let (context, shader_version, dpi_factor, glutin_window) = {
let glutin_window = glutin::ContextBuilder::new()
.with_stencil_buffer(0)
.with_depth_buffer(24)
.with_gl(glutin::GlRequest::GlThenGles {
opengl_version: (3, 2),
opengles_version: (3, 0),
})
.with_gl_profile(glutin::GlProfile::Core)
.with_vsync(vsync)
.build_windowed(window_builder, &events_loop)
.expect("Could not create glutin window.");
let dpi_factor = glutin_window.window().scale_factor();
let renderer = render::Renderer::new(resource_manager.clone());
let mut ui_container = ui::Container::new();
let glutin_window = unsafe {
glutin_window
.make_current()
.expect("Could not set current context.")
};
let context = unsafe {
glow::Context::from_loader_function(|s| glutin_window.get_proc_address(s) as *const _)
};
let shader_version = match glutin_window.get_api() {
glutin::Api::OpenGl => "#version 150", // OpenGL 3.2
glutin::Api::OpenGlEs => "#version 300 es", // OpenGL ES 3.0 (similar to WebGL 2)
glutin::Api::WebGl => {
panic!("unexpectedly received WebGl API with glutin, expected to use glow codepath")
}
};
(context, shader_version, dpi_factor, glutin_window)
};
gl::init(context);
info!("Shader version: {}", shader_version);
let renderer = render::Renderer::new(resource_manager.clone(), shader_version);
let ui_container = ui::Container::new();
let mut last_frame = Instant::now();
let frame_time = 1e9f64 / 60.0;
let mut screen_sys = screen::ScreenSystem::new();
if opt.server.is_none() {
@ -296,7 +355,6 @@ fn main2() {
}
let textures = renderer.get_textures();
let dpi_factor = window.window().scale_factor();
let default_protocol_version = protocol::versions::protocol_name_to_protocol_version(
opt.default_protocol_version
.unwrap_or_else(|| "".to_string()),
@ -328,110 +386,201 @@ fn main2() {
protocol::enable_network_debug();
}
if let Some(filename) = opt.network_parse_packet {
let data = fs::read(filename).unwrap();
protocol::try_parse_packet(data, default_protocol_version);
return;
}
if opt.server.is_some() {
game.connect_to(&opt.server.unwrap());
}
let mut last_resource_version = 0;
#[cfg(target_arch = "wasm32")]
let winit_window = Rc::new(RefCell::new(winit_window));
let game = Rc::new(RefCell::new(game));
let ui_container = Rc::new(RefCell::new(ui_container));
#[cfg(target_arch = "wasm32")]
{
let winit_window = Rc::clone(&winit_window);
let game = Rc::clone(&game);
let ui_container = Rc::clone(&ui_container);
render_loop.run(move |running: &mut bool| {
let winit_window = winit_window.borrow_mut();
let mut game = game.borrow_mut();
let mut ui_container = ui_container.borrow_mut();
tick_all(
&winit_window,
&mut game,
&mut ui_container,
&mut last_frame,
&mut resui,
&mut last_resource_version,
&mut vsync,
);
println!("render_loop");
});
}
#[cfg(target_arch = "wasm32")]
let winit_window = Rc::clone(&winit_window);
let game = Rc::clone(&game);
let ui_container = Rc::clone(&ui_container);
events_loop.run(move |event, _event_loop, control_flow| {
*control_flow = glutin::event_loop::ControlFlow::Poll;
#[cfg(target_arch = "wasm32")]
let winit_window = winit_window.borrow_mut();
if !handle_window_event(&mut window, &mut game, &mut ui_container, event) {
#[cfg(not(target_arch = "wasm32"))]
let winit_window = glutin_window.window();
let mut game = game.borrow_mut();
let mut ui_container = ui_container.borrow_mut();
#[cfg(target_arch = "wasm32")]
{
*control_flow = winit::event_loop::ControlFlow::Wait;
}
#[cfg(not(target_arch = "wasm32"))]
{
*control_flow = winit::event_loop::ControlFlow::Poll;
}
#[cfg(not(target_arch = "wasm32"))]
if let winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::Resized(physical_size),
..
} = event
{
glutin_window.resize(physical_size);
}
if !handle_window_event(&winit_window, &mut game, &mut ui_container, event) {
return;
}
let now = Instant::now();
let diff = now.duration_since(last_frame);
last_frame = now;
let delta = (diff.subsec_nanos() as f64) / frame_time;
let physical_size = window.window().inner_size();
let (physical_width, physical_height) = physical_size.into();
let (width, height) = physical_size.to_logical::<f64>(game.dpi_factor).into();
#[cfg(not(target_arch = "wasm32"))]
{
tick_all(
&winit_window,
&mut game,
&mut ui_container,
&mut last_frame,
&mut resui,
&mut last_resource_version,
&mut vsync,
);
let version = {
let try_res = game.resource_manager.try_write();
if let Ok(mut res) = try_res {
res.tick(&mut resui, &mut ui_container, delta);
res.version()
} else {
// TODO: why does game.resource_manager.write() sometimes deadlock?
//warn!("Failed to obtain mutable reference to resource manager!");
last_resource_version
}
};
last_resource_version = version;
let vsync_changed = *game.vars.get(settings::R_VSYNC);
if vsync != vsync_changed {
error!("Changing vsync currently requires restarting");
game.should_close = true;
// TODO: after https://github.com/tomaka/glutin/issues/693 Allow changing vsync on a Window
//vsync = vsync_changed;
glutin_window
.swap_buffers()
.expect("Failed to swap GL buffers");
}
let fps_cap = *game.vars.get(settings::R_MAX_FPS);
game.tick(delta);
game.server.tick(&mut game.renderer, delta);
// Check if window is valid, it might be minimized
if physical_width == 0 || physical_height == 0 {
return;
}
game.renderer.update_camera(physical_width, physical_height);
game.server.world.compute_render_list(&mut game.renderer);
game.chunk_builder
.tick(&mut game.server.world, &mut game.renderer, version);
game.screen_sys
.tick(delta, &mut game.renderer, &mut ui_container);
game.console
.lock()
.unwrap()
.tick(&mut ui_container, &game.renderer, delta, width as f64);
ui_container.tick(&mut game.renderer, delta, width as f64, height as f64);
game.renderer.tick(
&mut game.server.world,
delta,
width,
height,
physical_width,
physical_height,
);
if fps_cap > 0 && !vsync {
let frame_time = now.elapsed();
let sleep_interval = Duration::from_millis(1000 / fps_cap as u64);
if frame_time < sleep_interval {
thread::sleep(sleep_interval - frame_time);
}
}
window.swap_buffers().expect("Failed to swap GL buffers");
if game.should_close {
*control_flow = glutin::event_loop::ControlFlow::Exit;
*control_flow = winit::event_loop::ControlFlow::Exit;
}
});
}
fn tick_all(
window: &winit::window::Window,
game: &mut Game,
mut ui_container: &mut ui::Container,
last_frame: &mut Instant,
mut resui: &mut resources::ManagerUI,
last_resource_version: &mut usize,
vsync: &mut bool,
) {
let now = Instant::now();
let diff = now.duration_since(*last_frame);
*last_frame = now;
let frame_time = 1e9f64 / 60.0;
let delta = (diff.subsec_nanos() as f64) / frame_time;
let physical_size = window.inner_size();
let (physical_width, physical_height) = physical_size.into();
let (width, height) = physical_size.to_logical::<f64>(game.dpi_factor).into();
let version = {
let try_res = game.resource_manager.try_write();
if let Ok(mut res) = try_res {
res.tick(&mut resui, &mut ui_container, delta);
res.version()
} else {
// TODO: why does game.resource_manager.write() sometimes deadlock?
//warn!("Failed to obtain mutable reference to resource manager!");
*last_resource_version
}
};
*last_resource_version = version;
let vsync_changed = *game.vars.get(settings::R_VSYNC);
if *vsync != vsync_changed {
error!("Changing vsync currently requires restarting");
game.should_close = true;
// TODO: after https://github.com/tomaka/glutin/issues/693 Allow changing vsync on a Window
//vsync = vsync_changed;
}
let fps_cap = *game.vars.get(settings::R_MAX_FPS);
game.tick(delta);
game.server.tick(&mut game.renderer, delta);
// Check if window is valid, it might be minimized
if physical_width == 0 || physical_height == 0 {
return;
}
game.renderer.update_camera(physical_width, physical_height);
game.server.world.compute_render_list(&mut game.renderer);
game.chunk_builder
.tick(&mut game.server.world, &mut game.renderer, version);
game.screen_sys
.tick(delta, &mut game.renderer, &mut ui_container);
game.console
.lock()
.unwrap()
.tick(&mut ui_container, &game.renderer, delta, width);
ui_container.tick(&mut game.renderer, delta, width, height);
game.renderer.tick(
&mut game.server.world,
delta,
width as u32,
height as u32,
physical_width,
physical_height,
);
if fps_cap > 0 && !*vsync {
let frame_time = now.elapsed();
let sleep_interval = Duration::from_millis(1000 / fps_cap as u64);
if frame_time < sleep_interval {
thread::sleep(sleep_interval - frame_time);
}
}
}
fn handle_window_event<T>(
window: &mut glutin::WindowedContext<glutin::PossiblyCurrent>,
window: &winit::window::Window,
game: &mut Game,
ui_container: &mut ui::Container,
event: glutin::event::Event<T>,
event: winit::event::Event<T>,
) -> bool {
use glutin::event::*;
use winit::event::*;
match event {
Event::MainEventsCleared => return true,
Event::DeviceEvent { event, .. } => match event {
DeviceEvent::ModifiersChanged(modifiers_state) => {
game.is_ctrl_pressed = modifiers_state.ctrl();
game.is_logo_pressed = modifiers_state.logo();
}
DeviceEvent::MouseMotion {
Event::DeviceEvent { event, .. } => {
if let DeviceEvent::MouseMotion {
delta: (xrel, yrel),
} => {
} = event
{
let (rx, ry) = if xrel > 1000.0 || yrel > 1000.0 {
// Heuristic for if we were passed an absolute value instead of relative
// Workaround https://github.com/tomaka/glutin/issues/1084 MouseMotion event returns absolute instead of relative values, when running Linux in a VM
@ -453,8 +602,8 @@ fn handle_window_event<T>(
use std::f64::consts::PI;
if game.focused {
window.window().set_cursor_grab(true).unwrap();
window.window().set_cursor_visible(false);
window.set_cursor_grab(true).unwrap();
window.set_cursor_visible(false);
if let Some(player) = game.server.player {
let rotation = game
.server
@ -471,20 +620,19 @@ fn handle_window_event<T>(
}
}
} else {
window.window().set_cursor_grab(false).unwrap();
window.window().set_cursor_visible(true);
window.set_cursor_grab(false).unwrap();
window.set_cursor_visible(true);
}
}
_ => (),
},
}
Event::WindowEvent { event, .. } => {
match event {
WindowEvent::CloseRequested => game.should_close = true,
WindowEvent::Resized(physical_size) => {
window.resize(physical_size);
WindowEvent::ModifiersChanged(modifiers_state) => {
game.is_ctrl_pressed = modifiers_state.ctrl();
game.is_logo_pressed = modifiers_state.logo();
}
WindowEvent::CloseRequested => game.should_close = true,
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
game.dpi_factor = scale_factor;
}
@ -497,22 +645,22 @@ fn handle_window_event<T>(
WindowEvent::MouseInput { state, button, .. } => match (state, button) {
(ElementState::Released, MouseButton::Left) => {
let (width, height) = window
.window()
.inner_size()
.to_logical::<f64>(game.dpi_factor)
.into();
let physical_size = window.inner_size();
let (width, height) =
physical_size.to_logical::<f64>(game.dpi_factor).into();
if game.server.is_connected()
&& !game.focused
&& !game.screen_sys.is_current_closable()
{
game.focused = true;
window.window().set_cursor_grab(true).unwrap();
window.window().set_cursor_visible(false);
window.set_cursor_grab(true).unwrap();
window.set_cursor_visible(false);
} else if !game.focused {
window.window().set_cursor_grab(false).unwrap();
window.window().set_cursor_visible(true);
#[cfg(not(target_arch = "wasm32"))]
// TODO: after Pointer Lock https://github.com/rust-windowing/winit/issues/1674
window.set_cursor_grab(false).unwrap();
window.set_cursor_visible(true);
ui_container.click_at(
game,
game.last_mouse_x,
@ -530,16 +678,14 @@ fn handle_window_event<T>(
(_, _) => (),
},
WindowEvent::CursorMoved { position, .. } => {
let (x, y) = position.into();
let (x, y) = position.to_logical::<f64>(game.dpi_factor).into();
game.last_mouse_x = x;
game.last_mouse_y = y;
if !game.focused {
let (width, height) = window
.window()
.inner_size()
.to_logical::<f64>(game.dpi_factor)
.into();
let physical_size = window.inner_size();
let (width, height) =
physical_size.to_logical::<f64>(game.dpi_factor).into();
ui_container.hover_at(game, x, y, width, height);
}
}
@ -559,15 +705,15 @@ fn handle_window_event<T>(
match (input.state, input.virtual_keycode) {
(ElementState::Released, Some(VirtualKeyCode::Escape)) => {
if game.focused {
window.window().set_cursor_grab(false).unwrap();
window.window().set_cursor_visible(true);
window.set_cursor_grab(false).unwrap();
window.set_cursor_visible(true);
game.focused = false;
game.screen_sys.replace_screen(Box::new(
screen::SettingsMenu::new(game.vars.clone(), true),
));
} else if game.screen_sys.is_current_closable() {
window.window().set_cursor_grab(true).unwrap();
window.window().set_cursor_visible(false);
window.set_cursor_grab(true).unwrap();
window.set_cursor_visible(false);
game.focused = true;
game.screen_sys.pop_screen();
}
@ -579,13 +725,11 @@ fn handle_window_event<T>(
if !game.is_fullscreen {
// TODO: support options for exclusive and simple fullscreen
// see https://docs.rs/glutin/0.22.0-alpha5/glutin/window/struct.Window.html#method.set_fullscreen
window.window().set_fullscreen(Some(
glutin::window::Fullscreen::Borderless(
window.window().current_monitor(),
),
));
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(
window.current_monitor(),
)));
} else {
window.window().set_fullscreen(None);
window.set_fullscreen(None);
}
game.is_fullscreen = !game.is_fullscreen;

View File

@ -1114,11 +1114,7 @@ fn calculate_light(
let lz = (z + oz + dz).round() as i32;
let mut bl = snapshot.get_block_light(lx, ly, lz);
let mut sl = snapshot.get_sky_light(lx, ly, lz);
if (force
&& match snapshot.get_block(lx, ly, lz) {
block::Air {} => false,
_ => true,
})
if (force && !matches!(snapshot.get_block(lx, ly, lz), block::Air {}))
|| (sl == 0 && bl == 0)
{
bl = s_block_light;

View File

@ -46,7 +46,7 @@ impl Clouds {
v.set_source(&vertex);
v.compile();
if v.get_parameter(gl::COMPILE_STATUS) == 0 {
if !v.get_shader_compile_status() {
error!("Src: {}", vertex);
panic!("Shader error: {}", v.get_info_log());
} else {
@ -61,7 +61,7 @@ impl Clouds {
g.set_source(&geo);
g.compile();
if g.get_parameter(gl::COMPILE_STATUS) == 0 {
if !g.get_shader_compile_status() {
error!("Src: {}", geo);
panic!("Shader error: {}", g.get_info_log());
} else {
@ -76,7 +76,7 @@ impl Clouds {
f.set_source(&fragment);
f.compile();
if f.get_parameter(gl::COMPILE_STATUS) == 0 {
if !f.get_shader_compile_status() {
error!("Src: {}", fragment);
panic!("Shader error: {}", f.get_info_log());
} else {

View File

@ -17,11 +17,15 @@ use std::collections::HashMap;
#[derive(Default)]
pub struct Registry {
shaders: HashMap<String, String>,
shader_version: String,
}
impl Registry {
pub fn new() -> Registry {
Default::default()
pub fn new(shader_version: &str) -> Registry {
Registry {
shaders: Default::default(),
shader_version: shader_version.to_string(),
}
}
pub fn register(&mut self, name: &str, source: &str) {
@ -32,19 +36,32 @@ impl Registry {
.insert(name.to_owned(), source.trim().to_owned());
}
fn add_version(&self, out: &mut String) {
out.push_str(&self.shader_version);
out.push('\n');
if self.shader_version.ends_with(" es") {
out.push_str(
r#"precision mediump float;
precision mediump sampler2DArray;
#define ES
"#,
);
}
}
pub fn get(&self, name: &str) -> String {
let mut out = String::new();
out.push_str("#version 150\n");
self.add_version(&mut out);
self.get_internal(&mut out, name);
out
}
pub fn get_define(&self, name: &str, define: &str) -> String {
let mut out = String::new();
out.push_str("#version 150\n");
self.add_version(&mut out);
out.push_str("#define ");
out.push_str(define);
out.push_str("\n");
out.push('\n');
self.get_internal(&mut out, name);
out
}
@ -52,13 +69,13 @@ impl Registry {
fn get_internal(&self, out: &mut String, name: &str) {
let src = self.shaders.get(name).unwrap();
for line in src.lines() {
if line.starts_with("#include ") {
let inc = line["#include ".len()..].trim();
if let Some(stripped) = line.strip_prefix("#include ") {
let inc = stripped.trim();
self.get_internal(out, inc);
continue;
}
out.push_str(line);
out.push_str("\n");
out.push('\n');
}
}
}

View File

@ -37,13 +37,8 @@ use std::sync::atomic::{AtomicIsize, Ordering};
use std::sync::mpsc;
use std::thread;
#[cfg(not(target_arch = "wasm32"))]
const ATLAS_SIZE: usize = 1024;
// TEMP
const NUM_SAMPLES: i32 = 2;
pub struct Camera {
pub pos: cgmath::Point3<f64>,
pub yaw: f64,
@ -56,7 +51,7 @@ pub struct Renderer {
textures: Arc<RwLock<TextureManager>>,
pub ui: ui::UIState,
pub model: model::Manager,
pub clouds: clouds::Clouds,
pub clouds: Option<clouds::Clouds>,
gl_texture: gl::Texture,
texture_layers: usize,
@ -153,7 +148,7 @@ init_shader! {
}
impl Renderer {
pub fn new(res: Arc<RwLock<resources::Manager>>) -> Renderer {
pub fn new(res: Arc<RwLock<resources::Manager>>, shader_version: &str) -> Renderer {
let version = { res.read().unwrap().version() };
let tex = gl::Texture::new();
tex.bind(gl::TEXTURE_2D_ARRAY);
@ -175,7 +170,7 @@ impl Renderer {
let (textures, skin_req, skin_reply) = TextureManager::new(res.clone());
let textures = Arc::new(RwLock::new(textures));
let mut greg = glsl::Registry::new();
let mut greg = glsl::Registry::new(shader_version);
shaders::add_shaders(&mut greg);
let ui = ui::UIState::new(&greg, textures.clone(), res.clone());
@ -196,10 +191,18 @@ impl Renderer {
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::depth_func(gl::LESS_OR_EQUAL);
#[cfg(not(target_arch = "wasm32"))]
let clouds = Some(clouds::Clouds::new(&greg, textures.clone()));
// No clouds on wasm since no geo shaders on WebGL
// TODO: setting to disable clouds on native, too, if desired
#[cfg(target_arch = "wasm32")]
let clouds = None;
Renderer {
resource_version: version,
model: model::Manager::new(&greg),
clouds: clouds::Clouds::new(&greg, textures.clone()),
clouds,
textures,
ui,
resources: res,
@ -313,6 +316,7 @@ impl Renderer {
gl::active_texture(0);
self.gl_texture.bind(gl::TEXTURE_2D_ARRAY);
#[cfg(not(target_arch = "wasm32"))]
gl::enable(gl::MULTISAMPLE);
let time_offset = self.sky_offset * 0.9;
@ -363,17 +367,19 @@ impl Renderer {
self.light_level,
self.sky_offset,
);
if world.copy_cloud_heightmap(&mut self.clouds.heightmap_data) {
self.clouds.dirty = true;
if let Some(clouds) = &mut self.clouds {
if world.copy_cloud_heightmap(&mut clouds.heightmap_data) {
clouds.dirty = true;
}
clouds.draw(
&self.camera.pos,
&self.perspective_matrix,
&self.camera_matrix,
self.light_level,
self.sky_offset,
delta,
);
}
self.clouds.draw(
&self.camera.pos,
&self.perspective_matrix,
&self.camera_matrix,
self.light_level,
self.sky_offset,
delta,
);
// Trans chunk rendering
self.chunk_shader_alpha.program.use_program();
@ -412,8 +418,8 @@ impl Renderer {
trans.trans.bind();
gl::clear_color(0.0, 0.0, 0.0, 1.0);
gl::clear(gl::ClearFlags::Color);
gl::clear_buffer(gl::COLOR, 0, &[0.0, 0.0, 0.0, 1.0]);
gl::clear_buffer(gl::COLOR, 1, &[0.0, 0.0, 0.0, 0.0]);
gl::clear_buffer(gl::COLOR, 0, &mut [0.0, 0.0, 0.0, 1.0]);
gl::clear_buffer(gl::COLOR, 1, &mut [0.0, 0.0, 0.0, 0.0]);
gl::blend_func_separate(
gl::ONE_FACTOR,
gl::ONE_FACTOR,
@ -448,6 +454,8 @@ impl Renderer {
gl::enable(gl::DEPTH_TEST);
gl::depth_mask(true);
gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
#[cfg(not(target_arch = "wasm32"))]
gl::disable(gl::MULTISAMPLE);
self.ui.tick(width, height);
@ -777,7 +785,6 @@ init_shader! {
required accum => "taccum",
required revealage => "trevealage",
required color => "tcolor",
required samples => "samples",
},
}
}
@ -805,7 +812,7 @@ impl TransInfo {
None,
);
accum.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
accum.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAX_LEVEL, gl::LINEAR);
accum.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
trans.texture_2d(gl::COLOR_ATTACHMENT_0, gl::TEXTURE_2D, &accum, 0);
let revealage = gl::Texture::new();
@ -821,7 +828,7 @@ impl TransInfo {
None,
);
revealage.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
revealage.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAX_LEVEL, gl::LINEAR);
revealage.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
trans.texture_2d(gl::COLOR_ATTACHMENT_1, gl::TEXTURE_2D, &revealage, 0);
let trans_depth = gl::Texture::new();
@ -833,55 +840,58 @@ impl TransInfo {
height,
gl::DEPTH_COMPONENT24,
gl::DEPTH_COMPONENT,
gl::UNSIGNED_BYTE,
gl::UNSIGNED_INT,
None,
);
trans_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
trans_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAX_LEVEL, gl::LINEAR);
trans_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
trans.texture_2d(gl::DEPTH_ATTACHMENT, gl::TEXTURE_2D, &trans_depth, 0);
chunk_shader.program.use_program();
gl::bind_frag_data_location(&chunk_shader.program, 0, "accum");
gl::bind_frag_data_location(&chunk_shader.program, 1, "revealage");
#[cfg(not(target_arch = "wasm32"))] // bound with layout(location=)
{
gl::bind_frag_data_location(&chunk_shader.program, 0, "accum");
gl::bind_frag_data_location(&chunk_shader.program, 1, "revealage");
}
gl::check_framebuffer_status();
gl::draw_buffers(&[gl::COLOR_ATTACHMENT_0, gl::COLOR_ATTACHMENT_1]);
let main = gl::Framebuffer::new();
main.bind();
// TODO: support rendering to a multisample renderbuffer for MSAA, using glRenderbufferStorageMultisample
// https://github.com/iceiix/stevenarella/pull/442
let fb_color = gl::Texture::new();
fb_color.bind(gl::TEXTURE_2D_MULTISAMPLE);
fb_color.image_2d_sample(
gl::TEXTURE_2D_MULTISAMPLE,
NUM_SAMPLES,
fb_color.bind(gl::TEXTURE_2D);
fb_color.image_2d(
gl::TEXTURE_2D,
0,
width,
height,
gl::RGBA8,
false,
);
main.texture_2d(
gl::COLOR_ATTACHMENT_0,
gl::TEXTURE_2D_MULTISAMPLE,
&fb_color,
0,
gl::RGBA,
gl::UNSIGNED_BYTE,
None,
);
fb_color.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
fb_color.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
main.texture_2d(gl::COLOR_ATTACHMENT_0, gl::TEXTURE_2D, &fb_color, 0);
let fb_depth = gl::Texture::new();
fb_depth.bind(gl::TEXTURE_2D_MULTISAMPLE);
fb_depth.image_2d_sample(
gl::TEXTURE_2D_MULTISAMPLE,
NUM_SAMPLES,
fb_depth.bind(gl::TEXTURE_2D);
fb_depth.image_2d_ex(
gl::TEXTURE_2D,
0,
width,
height,
gl::DEPTH_COMPONENT24,
false,
);
main.texture_2d(
gl::DEPTH_ATTACHMENT,
gl::TEXTURE_2D_MULTISAMPLE,
&fb_depth,
0,
gl::DEPTH_COMPONENT,
gl::UNSIGNED_INT,
None,
);
fb_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR);
fb_depth.set_parameter(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR);
main.texture_2d(gl::DEPTH_ATTACHMENT, gl::TEXTURE_2D, &fb_depth, 0);
gl::check_framebuffer_status();
gl::unbind_framebuffer();
@ -925,13 +935,12 @@ impl TransInfo {
gl::active_texture(1);
self.revealage.bind(gl::TEXTURE_2D);
gl::active_texture(2);
self.fb_color.bind(gl::TEXTURE_2D_MULTISAMPLE);
self.fb_color.bind(gl::TEXTURE_2D);
shader.program.use_program();
shader.accum.set_int(0);
shader.revealage.set_int(1);
shader.color.set_int(2);
shader.samples.set_int(NUM_SAMPLES);
self.array.bind();
gl::draw_arrays(gl::TRIANGLES, 0, 6);
}
@ -951,7 +960,7 @@ pub struct TextureManager {
skins: HashMap<String, AtomicIsize, BuildHasherDefault<FNVHash>>,
_skin_thread: thread::JoinHandle<()>,
_skin_thread: Option<thread::JoinHandle<()>>,
}
impl TextureManager {
@ -966,7 +975,13 @@ impl TextureManager {
) {
let (tx, rx) = mpsc::channel();
let (stx, srx) = mpsc::channel();
let skin_thread = thread::spawn(|| Self::process_skins(srx, tx));
#[cfg(target_arch = "wasm32")]
let skin_thread = None;
#[cfg(not(target_arch = "wasm32"))]
let skin_thread = Some(thread::spawn(|| Self::process_skins(srx, tx)));
let mut tm = TextureManager {
textures: HashMap::with_hasher(BuildHasherDefault::default()),
version: {
@ -1143,8 +1158,7 @@ impl TextureManager {
self.add_defaults();
for name in map.keys() {
if name.starts_with("steven-dynamic:") {
let n = &name["steven-dynamic:".len()..];
if let Some(n) = name.strip_prefix("steven-dynamic:") {
let (width, height, data) = {
let dynamic_texture = match self.dynamic_textures.get(n) {
Some(val) => val,
@ -1152,7 +1166,7 @@ impl TextureManager {
};
let img = &dynamic_texture.1;
let (width, height) = img.dimensions();
(width, height, img.to_rgba().into_vec())
(width, height, img.to_rgba8().into_vec())
};
let new_tex =
self.put_texture("steven-dynamic", n, width as u32, height as u32, data);
@ -1213,7 +1227,7 @@ impl TextureManager {
};
self.pending_uploads
.push((tex.atlas, rect, img.to_rgba().into_vec()));
.push((tex.atlas, rect, img.to_rgba8().into_vec()));
self.dynamic_textures
.get_mut(&format!("skin-{}", hash))
.unwrap()
@ -1243,7 +1257,7 @@ impl TextureManager {
let (width, height) = img.dimensions();
// Might be animated
if (name.starts_with("blocks/") || name.starts_with("items/")) && width != height {
let id = img.to_rgba().into_vec();
let id = img.to_rgba8().into_vec();
let frame = id[..(width * width * 4) as usize].to_owned();
if let Some(mut ani) = self.load_animation(plugin, name, &img, id) {
ani.texture = self.put_texture(plugin, name, width, width, frame);
@ -1251,7 +1265,7 @@ impl TextureManager {
return;
}
}
self.put_texture(plugin, name, width, height, img.to_rgba().into_vec());
self.put_texture(plugin, name, width, height, img.to_rgba8().into_vec());
return;
}
}
@ -1332,7 +1346,7 @@ impl TextureManager {
let mut full_name = String::new();
full_name.push_str(plugin);
full_name.push_str(":");
full_name.push(':');
full_name.push_str(name);
let tex = Texture {
@ -1372,7 +1386,7 @@ impl TextureManager {
let mut full_name = String::new();
full_name.push_str(plugin);
full_name.push_str(":");
full_name.push(':');
full_name.push_str(name);
let t = Texture {
@ -1406,7 +1420,7 @@ impl TextureManager {
rect_pos = Some(i);
}
}
let data = img.to_rgba().into_vec();
let data = img.to_rgba8().into_vec();
if let Some(rect_pos) = rect_pos {
let mut tex = self.free_dynamics.remove(rect_pos);

View File

@ -114,7 +114,7 @@ impl Manager {
v.vertex_pointer(4, gl::UNSIGNED_BYTE, true, 36, 28)
}
if let Some(v) = collection.shader.id {
v.vertex_pointer_int(1, gl::UNSIGNED_BYTE, 36, 32)
v.vertex_pointer_int(1, gl::BYTE, 36, 32)
}
let mut model = Model {
@ -249,19 +249,19 @@ impl Manager {
gl::enable(gl::BLEND);
for collection in &self.collections {
collection.shader.program.use_program();
if let Some(v) = collection.shader.perspective_matrix {
if let Some(v) = &collection.shader.perspective_matrix {
v.set_matrix4(perspective_matrix)
}
if let Some(v) = collection.shader.camera_matrix {
if let Some(v) = &collection.shader.camera_matrix {
v.set_matrix4(camera_matrix)
}
if let Some(v) = collection.shader.texture {
if let Some(v) = &collection.shader.texture {
v.set_int(0)
}
if let Some(v) = collection.shader.sky_offset {
if let Some(v) = &collection.shader.sky_offset {
v.set_float(sky_offset)
}
if let Some(v) = collection.shader.light_level {
if let Some(v) = &collection.shader.light_level {
v.set_float(light_level)
}
gl::blend_func(collection.blend_s, collection.blend_d);
@ -276,16 +276,14 @@ impl Manager {
continue;
}
model.array.bind();
if let Some(v) = collection.shader.lighting {
if let Some(v) = &collection.shader.lighting {
v.set_float2(model.block_light, model.sky_light)
}
if let Some(v) = collection.shader.model_matrix {
if let Some(v) = &collection.shader.model_matrix {
v.set_matrix4_multi(&model.matrix)
}
if let Some(v) = collection.shader.color_mul {
unsafe {
v.set_float_multi_raw(model.colors.as_ptr() as *const _, model.colors.len())
}
if let Some(v) = &collection.shader.color_mul {
v.set_float_multi(&model.colors)
}
gl::draw_elements(gl::TRIANGLES, model.count, self.index_type, 0);
}

View File

@ -131,7 +131,7 @@ pub fn create_program(vertex: &str, fragment: &str) -> gl::Program {
v.set_source(vertex);
v.compile();
if v.get_parameter(gl::COMPILE_STATUS) == 0 {
if !v.get_shader_compile_status() {
error!("Src: {}", vertex);
panic!("Shader error: {}", v.get_info_log());
} else {
@ -146,7 +146,7 @@ pub fn create_program(vertex: &str, fragment: &str) -> gl::Program {
f.set_source(fragment);
f.compile();
if f.get_parameter(gl::COMPILE_STATUS) == 0 {
if !f.get_shader_compile_status() {
error!("Src: {}", fragment);
panic!("Shader error: {}", f.get_info_log());
} else {

View File

@ -7,12 +7,23 @@ in float vAtlas;
in vec3 vLighting;
#ifndef alpha
#ifdef ES
layout(location = 2) out vec4 fragColor;
#else
out vec4 fragColor;
#endif
#else
#ifdef ES
layout(location = 0) out vec4 accum;
layout(location = 1) out float revealage;
#else
out vec4 accum;
out float revealage;
#endif
#endif
#include lookup_texture
void main() {

View File

@ -20,7 +20,7 @@ out vec3 vLighting;
void main() {
vec3 pos = vec3(aPosition.x, -aPosition.y, aPosition.z);
vec3 o = vec3(offset.x, -offset.y / 4096.0, offset.z);
vec3 o = vec3(float(offset.x), -float(offset.y) / 4096.0, float(offset.z));
gl_Position = perspectiveMatrix * cameraMatrix * vec4(pos + o * 16.0, 1.0);
vColor = aColor;

View File

@ -1,4 +1,4 @@
const float invAtlasSize = 1.0 / 1024;
const float invAtlasSize = 1.0 / 1024.0;
vec4 atlasTexture() {
vec2 tPos = vTextureOffset;
tPos = clamp(tPos, vec2(0.1), vTextureInfo.zw - 0.1);

View File

@ -26,8 +26,8 @@ void main() {
vColor = aColor;
vTextureInfo = aTextureInfo;
vTextureOffset = aTextureOffset.xy / 16.0;
vAtlas = aTextureOffset.z;
vTextureOffset = vec2(aTextureOffset.xy) / 16.0;
vAtlas = float(aTextureOffset.z);
vID = float(id);
vLighting = getLight(lighting);

View File

@ -21,7 +21,7 @@ void main() {
gl_Position = perspectiveMatrix * cameraMatrix * modelMatrix[id] * vec4(pos, 1.0);
vTextureInfo = aTextureInfo;
vTextureOffset = aTextureOffset.xy / 16.0;
vAtlas = aTextureOffset.z;
vTextureOffset = vec2(aTextureOffset.xy) / 16.0;
vAtlas = float(aTextureOffset.z);
vID = float(id);
}

View File

@ -1,8 +1,6 @@
uniform sampler2D taccum;
uniform sampler2D trevealage;
uniform sampler2DMS tcolor;
uniform int samples;
uniform sampler2D tcolor;
out vec4 fragColor;
@ -12,11 +10,6 @@ void main() {
float aa = texelFetch(trevealage, C, 0).r;
vec4 col = texelFetch(tcolor, C, 0);
for (int i = 1; i < samples; i++) {
col += texelFetch(tcolor, C, i);
}
col /= float(samples);
float r = accum.a;
accum.a = aa;
if (r >= 1.0) {

View File

@ -11,10 +11,10 @@ out float vAtlas;
uniform vec2 screenSize;
void main() {
vec2 pos = aPosition.xy / screenSize;
vec2 pos = vec2(aPosition.xy) / screenSize;
gl_Position = vec4((pos.x-0.5)*2.0, -(pos.y-0.5)*2.0, float(-aPosition.z) / float(0xFFFF-1), 1.0);
vColor = aColor;
vTextureInfo = aTextureInfo;
vTextureOffset = aTextureOffset.xy / 16.0;
vAtlas = aTextureOffset.z;
}
vTextureOffset = vec2(aTextureOffset.xy) / 16.0;
vAtlas = float(aTextureOffset.z);
}

View File

@ -175,7 +175,7 @@ impl super::Screen for Login {
let mut client_token = self.vars.get(auth::AUTH_CLIENT_TOKEN).clone();
if client_token.is_empty() {
client_token = std::iter::repeat(())
.map(|()| rand::thread_rng().sample(&rand::distributions::Alphanumeric))
.map(|()| rand::thread_rng().sample(&rand::distributions::Alphanumeric) as char)
.take(20)
.collect();
self.vars.set(auth::AUTH_CLIENT_TOKEN, client_token);

View File

@ -24,8 +24,8 @@ use crate::protocol;
use crate::render;
use crate::ui;
use instant::Duration;
use rand::Rng;
use std::time::Duration;
pub struct ServerList {
elements: Option<UIElements>,
@ -528,6 +528,7 @@ impl super::Screen for ServerList {
let name: String = std::iter::repeat(())
.map(|()| {
rand::thread_rng().sample(&rand::distributions::Alphanumeric)
as char
})
.take(30)
.collect();

View File

@ -509,6 +509,7 @@ impl Server {
self pck {
PluginMessageClientbound_i16 => on_plugin_message_clientbound_i16,
PluginMessageClientbound => on_plugin_message_clientbound_1,
JoinGame_WorldNames_IsHard => on_game_join_worldnames_ishard,
JoinGame_WorldNames => on_game_join_worldnames,
JoinGame_HashedSeed_Respawn => on_game_join_hashedseed_respawn,
JoinGame_i32_ViewDistance => on_game_join_i32_viewdistance,
@ -518,9 +519,11 @@ impl Server {
Respawn_Gamemode => on_respawn_gamemode,
Respawn_HashedSeed => on_respawn_hashedseed,
Respawn_WorldName => on_respawn_worldname,
Respawn_NBT => on_respawn_nbt,
KeepAliveClientbound_i64 => on_keep_alive_i64,
KeepAliveClientbound_VarInt => on_keep_alive_varint,
KeepAliveClientbound_i32 => on_keep_alive_i32,
ChunkData_Biomes3D_VarInt => on_chunk_data_biomes3d_varint,
ChunkData_Biomes3D_bool => on_chunk_data_biomes3d_bool,
ChunkData => on_chunk_data,
ChunkData_Biomes3D => on_chunk_data_biomes3d,
@ -533,6 +536,7 @@ impl Server {
ChunkUnload => on_chunk_unload,
BlockChange_VarInt => on_block_change_varint,
BlockChange_u8 => on_block_change_u8,
MultiBlockChange_Packed => on_multi_block_change_packed,
MultiBlockChange_VarInt => on_multi_block_change_varint,
MultiBlockChange_u16 => on_multi_block_change_u16,
TeleportPlayer_WithConfirm => on_teleport_player_withconfirm,
@ -970,6 +974,13 @@ impl Server {
}
}
fn on_game_join_worldnames_ishard(
&mut self,
join: packet::play::clientbound::JoinGame_WorldNames_IsHard,
) {
self.on_game_join(join.gamemode, join.entity_id)
}
fn on_game_join_worldnames(&mut self, join: packet::play::clientbound::JoinGame_WorldNames) {
self.on_game_join(join.gamemode, join.entity_id)
}
@ -1047,6 +1058,10 @@ impl Server {
self.respawn(respawn.gamemode)
}
fn on_respawn_nbt(&mut self, respawn: packet::play::clientbound::Respawn_NBT) {
self.respawn(respawn.gamemode)
}
fn respawn(&mut self, gamemode_u8: u8) {
self.world = world::World::new(self.protocol_version);
let gamemode = Gamemode::from_int((gamemode_u8 & 0x7) as i32);
@ -1760,23 +1775,46 @@ impl Server {
let x = block_entity.1.get("x").unwrap().as_int().unwrap();
let y = block_entity.1.get("y").unwrap().as_int().unwrap();
let z = block_entity.1.get("z").unwrap().as_int().unwrap();
let tile_id = block_entity.1.get("id").unwrap().as_str().unwrap();
let action;
match tile_id {
// Fake a sign update
"Sign" => action = 9,
// Not something we care about, so break the loop
_ => continue,
if let Some(tile_id) = block_entity.1.get("id") {
let tile_id = tile_id.as_str().unwrap();
let action;
match tile_id {
// Fake a sign update
"Sign" => action = 9,
// Not something we care about, so break the loop
_ => continue,
}
self.on_block_entity_update(packet::play::clientbound::UpdateBlockEntity {
location: Position::new(x, y, z),
action,
nbt: Some(block_entity.clone()),
});
} else {
warn!(
"Block entity at ({},{},{}) missing id tag: {:?}",
x, y, z, block_entity
);
}
self.on_block_entity_update(packet::play::clientbound::UpdateBlockEntity {
location: Position::new(x, y, z),
action,
nbt: Some(block_entity.clone()),
});
}
}
}
fn on_chunk_data_biomes3d_varint(
&mut self,
chunk_data: packet::play::clientbound::ChunkData_Biomes3D_VarInt,
) {
self.world
.load_chunk115(
chunk_data.chunk_x,
chunk_data.chunk_z,
chunk_data.new,
chunk_data.bitmask.0 as u16,
chunk_data.data.data,
)
.unwrap();
self.load_block_entities(chunk_data.block_entities.data);
}
fn on_chunk_data_biomes3d_bool(
&mut self,
chunk_data: packet::play::clientbound::ChunkData_Biomes3D_bool,
@ -1934,6 +1972,31 @@ impl Server {
);
}
fn on_multi_block_change_packed(
&mut self,
block_change: packet::play::clientbound::MultiBlockChange_Packed,
) {
let sx = (block_change.chunk_section_pos >> 42) as i32;
let sy = ((block_change.chunk_section_pos << 44) >> 44) as i32;
let sz = ((block_change.chunk_section_pos << 22) >> 42) as i32;
for record in block_change.records.data {
let block_raw_id = record.0 >> 12;
let lz = (record.0 & 0xf) as i32;
let ly = ((record.0 >> 4) & 0xf) as i32;
let lx = ((record.0 >> 8) & 0xf) as i32;
self.world.set_block(
Position::new(sx + lx as i32, sy + ly as i32, sz + lz as i32),
block::Block::by_vanilla_id(
block_raw_id as usize,
self.protocol_version,
&self.world.modded_block_ids,
),
);
}
}
fn on_multi_block_change_varint(
&mut self,
block_change: packet::play::clientbound::MultiBlockChange_VarInt,

View File

@ -1,6 +1,6 @@
use crate::console;
use glutin::event::VirtualKeyCode;
use std::marker::PhantomData;
use winit::event::VirtualKeyCode;
// Might just rename this to settings.rs
pub const R_MAX_FPS: console::CVar<i64> = console::CVar {

View File

@ -1,10 +1,10 @@
use crate::render;
use crate::resources;
use crate::ui;
use instant::Instant;
use rand::{self, seq::SliceRandom};
use std::f64::consts;
use std::sync::{Arc, RwLock};
use std::time::{SystemTime, UNIX_EPOCH};
pub struct Logo {
_shadow: ui::BatchRef,
@ -15,6 +15,7 @@ pub struct Logo {
text_orig_x: f64,
text_index: isize,
text_strings: Vec<String>,
started: Instant,
}
impl Logo {
@ -131,11 +132,12 @@ impl Logo {
text_orig_x,
text_index: -1,
text_strings,
started: Instant::now(),
}
}
pub fn tick(&mut self, renderer: &mut render::Renderer) {
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
let now = Instant::now().duration_since(self.started);
// Splash text
let text_index = (now.as_secs() / 15) as isize % self.text_strings.len() as isize;

View File

@ -18,9 +18,9 @@ use crate::format;
use crate::render;
#[cfg(not(target_arch = "wasm32"))]
use clipboard::{ClipboardContext, ClipboardProvider};
use glutin::event::VirtualKeyCode;
use std::cell::{RefCell, RefMut};
use std::rc::{Rc, Weak};
use winit::event::VirtualKeyCode;
const SCALED_WIDTH: f64 = 854.0;
const SCALED_HEIGHT: f64 = 480.0;

View File

@ -206,7 +206,7 @@ impl World {
#[allow(clippy::verbose_bit_mask)] // "llvm generates better code" for updates_performed & 0xFFF "on x86"
pub fn tick(&mut self, m: &mut ecs::Manager) {
use std::time::Instant;
use instant::Instant;
let start = Instant::now();
let mut updates_performed = 0;
while !self.light_updates.is_empty() {
@ -430,7 +430,7 @@ impl World {
y: i32,
z: i32,
) -> Option<(Option<&mut Section>, &mut u32)> {
if y < 0 || y > 15 {
if !(0..=15).contains(&y) {
return None;
}
if let Some(chunk) = self.chunks.get_mut(&CPos(x, z)) {
@ -540,7 +540,7 @@ impl World {
let z2 = min(16, max(0, z + d - (cz << 4)));
for cy in cy1..cy2 {
if cy < 0 || cy > 15 {
if !(0..=15).contains(&cy) {
continue;
}
let section = &chunk.sections[cy as usize];
@ -1066,7 +1066,8 @@ impl World {
}
let bits = LenPrefixed::<VarInt, u64>::read_from(&mut data)?.data;
let m = bit::Map::from_raw(bits, bit_size as usize);
let padded = self.protocol_version >= 736;
let m = bit::Map::from_raw(bits, bit_size as usize, padded);
for bi in 0..4096 {
let id = m.get(bi);
@ -1123,7 +1124,7 @@ impl World {
}
fn flag_section_dirty(&mut self, x: i32, y: i32, z: i32) {
if y < 0 || y > 15 {
if !(0..=15).contains(&y) {
return;
}
let cpos = CPos(x, z);
@ -1254,7 +1255,7 @@ impl Chunk {
fn set_block(&mut self, x: i32, y: i32, z: i32, b: block::Block) -> bool {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return false;
}
let s_idx = s_idx as usize;
@ -1296,7 +1297,7 @@ impl Chunk {
fn get_block(&self, x: i32, y: i32, z: i32) -> block::Block {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return block::Missing {};
}
match self.sections[s_idx as usize].as_ref() {
@ -1307,7 +1308,7 @@ impl Chunk {
fn get_block_light(&self, x: i32, y: i32, z: i32) -> u8 {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return 0;
}
match self.sections[s_idx as usize].as_ref() {
@ -1318,7 +1319,7 @@ impl Chunk {
fn set_block_light(&mut self, x: i32, y: i32, z: i32, light: u8) {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return;
}
let s_idx = s_idx as usize;
@ -1336,7 +1337,7 @@ impl Chunk {
fn get_sky_light(&self, x: i32, y: i32, z: i32) -> u8 {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return 15;
}
match self.sections[s_idx as usize].as_ref() {
@ -1347,7 +1348,7 @@ impl Chunk {
fn set_sky_light(&mut self, x: i32, y: i32, z: i32, light: u8) {
let s_idx = y >> 4;
if s_idx < 0 || s_idx > 15 {
if !(0..=15).contains(&s_idx) {
return;
}
let s_idx = s_idx as usize;

268
std_or_web/Cargo.lock generated
View File

@ -1,276 +1,14 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "base-x"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bumpalo"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "discard"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "hex"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "localstoragefs"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ryu"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde_derive"
version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sha1"
version = "0.6.0"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "std_or_web"
version = "0.0.1"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"localstoragefs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if",
]
[[package]]
name = "stdweb"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"stdweb-derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
"stdweb-internal-macros 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
"stdweb-internal-runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stdweb-derive"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stdweb-internal-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"base-x 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)",
"sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stdweb-internal-runtime"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasm-bindgen"
version = "0.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro-support 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-backend 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum base-x 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum discard 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum localstoragefs 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba6cbee6bbe7e6ea61ad375952239a9678624f216a0f442ae04a90260e76cdf"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
"checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64"
"checksum serde_json 1.0.44 (registry+https://github.com/rust-lang/crates.io-index)" = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7"
"checksum sha1 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
"checksum stdweb 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
"checksum stdweb-derive 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
"checksum stdweb-internal-macros 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
"checksum stdweb-internal-runtime 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
"checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum wasm-bindgen 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "99de4b68939a880d530aed51289a7c7baee154e3ea8ac234b542c49da7134aaf"
"checksum wasm-bindgen-backend 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "b58e66a093a7b7571cb76409763c495b8741ac4319ac20acc2b798f6766d92ee"
"checksum wasm-bindgen-macro 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "a80f89daea7b0a67b11f6e9f911422ed039de9963dce00048a653b63d51194bf"
"checksum wasm-bindgen-macro-support 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "4f9dbc3734ad6cff6b76b75b7df98c06982becd0055f651465a08f769bca5c61"
"checksum wasm-bindgen-shared 0.2.56 (registry+https://github.com/rust-lang/crates.io-index)" = "d907984f8506b3554eab48b8efff723e764ddbf76d4cd4a3fe4196bc00c49a70"

View File

@ -5,7 +5,4 @@ authors = [ "iceiix <ice_ix@protonmail.ch>" ]
edition = "2018"
[dependencies]
cfg-if = "0.1.10"
[target.'cfg(target_arch = "wasm32")'.dependencies]
localstoragefs = "0.1.0"
cfg-if = "1.0.0"

View File

@ -2,7 +2,52 @@ use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_arch = "wasm32")] {
pub use localstoragefs::fs;
// TODO: a real filesystem like https://emscripten.org/docs/api_reference/Filesystem-API.html
pub mod fs {
use std::convert::AsRef;
use std::io::Result;
use std::path::Path;
use std::io::{Read, Write};
pub struct File { }
impl File {
pub fn create<P: AsRef<Path>>(path: P) -> Result<File> {
println!("File create {}", path.as_ref().to_str().unwrap());
Ok(File{})
}
pub fn open<P: AsRef<Path>>(path: P) -> Result<File> {
println!("File open {}", path.as_ref().to_str().unwrap());
Err(std::io::Error::new(std::io::ErrorKind::Other, "No files exist on web"))
}
}
impl Read for File {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
println!("File read");
Ok(0)
}
}
impl Read for &File {
fn read(&mut self, _buf: &mut [u8]) -> Result<usize> {
println!("&File read");
Ok(0)
}
}
impl Write for File {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
println!("File write {:?}", buf);
Ok(buf.len())
}
fn flush(&mut self) -> Result<()> {
Ok(())
}
}
}
} else {
pub use std::fs;
}

View File

@ -2,12 +2,21 @@
Web app for running Stevenarella as WebAssembly
Status: very incomplete. It does not currently compile, due to required modifications to adapt to the web,
for progress see: [https://github.com/iceiix/stevenarella/issues/171](https://github.com/iceiix/stevenarella/issues/171).
Status: very incomplete. It currently compiles but does not run, due to required modifications to adapt to the web,
for progress see: [🕸️ Web support](https://github.com/iceiix/stevenarella/issues/446)
## Building
To build for wasm32-unknown-unknown, run:
To build for wasm32-unknown-unknown, run in the top-level directory (not www):
```sh
rustup target add wasm32-unknown-unknown
cargo install wasm-bindgen-cli
cargo install wasm-pack
wasm-pack build --dev
```
or:
```sh
cargo web start --target wasm32-unknown-unknown
@ -19,7 +28,7 @@ After building the Rust app, run the NodeJS web server as follows:
```sh
cd pkg
npm link
sudo npm link
cd ..
cd www
npm link stevenarella

View File

@ -1,3 +1,3 @@
import * as wasm from "stevenarella";
import * as wasm from "../pkg/stevenarella_bg.wasm";
wasm.main();

605
www/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -19,8 +19,8 @@
},
"homepage": "https://github.com/rustwasm/create-wasm-app#readme",
"devDependencies": {
"copy-webpack-plugin": "^5.1.1",
"webpack": "^4.43.0",
"copy-webpack-plugin": "^5.1.2",
"webpack": "^4.44.2",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0"
}