working through ownership hell once again

This commit is contained in:
Evan Pratten 2021-09-28 16:17:37 -04:00
parent 2c1d620de8
commit 36f0744e31
15 changed files with 213 additions and 57 deletions

View File

@ -18,6 +18,7 @@ rust-embed = "6.2.0"
raylib = "3.5"
puffin = "0.9"
puffin_http = "0.6"
dirty-fsm = "0.2"
[dev-dependencies]
puffin_viewer = "0.6"

14
game/src/context.rs Normal file
View File

@ -0,0 +1,14 @@
#[derive(Debug)]
pub struct GameContext {
}
impl GameContext {
/// Construct a new game context.
pub fn new() -> Self {
Self {
}
}
}

View File

@ -1 +1,2 @@
pub mod util;
pub mod render_layer;

View File

@ -0,0 +1,14 @@
use raylib::{RaylibHandle, prelude::{RaylibDrawHandle, RaylibMode2D}};
pub trait FrameUpdate {
fn update(&mut self, raylib: &RaylibHandle, delta_seconds: f32);
}
pub trait ScreenSpaceRender {
fn render_screen_space(&self, raylib: &mut RaylibDrawHandle);
}
pub trait WorldSpaceRender {
fn render_world_space(&self, raylib: &mut RaylibMode2D<RaylibDrawHandle>);
}

View File

@ -1,9 +1,9 @@
#![feature(derive_default_enum)]
use std::cell::RefCell;
use discord_sdk::activity::ActivityBuilder;
use raylib::prelude::*;
use shaders::{
shader::ShaderWrapper,
util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture},
};
use tracing::{error, info};
use utilities::{
datastore::StaticGameData,
@ -12,14 +12,24 @@ use utilities::{
math::rotate_vector,
};
use crate::{
context::GameContext,
scenes::{build_screen_state_machine, RenderContext},
utilities::shaders::{
shader::ShaderWrapper,
util::{dynamic_screen_texture::DynScreenTexture, render_texture::render_to_texture},
},
};
#[macro_use]
extern crate thiserror;
#[macro_use]
extern crate serde;
mod shaders;
mod utilities;
mod context;
mod gfx;
mod scenes;
mod utilities;
/// The game entrypoint
pub async fn game_begin() {
@ -30,12 +40,10 @@ pub async fn game_begin() {
.expect("Could not load general game config data");
// Set up profiling
// #[cfg(debug_assertions)]
// {
#[cfg(debug_assertions)]
let _puffin_server =
puffin_http::Server::new(&format!("0.0.0.0:{}", puffin_http::DEFAULT_PORT)).unwrap();
puffin::set_scopes_on(true);
// }
// Attempt to connect to a locally running Discord instance for rich presence access
let discord_config = DiscordConfig::load(
@ -60,6 +68,13 @@ pub async fn game_begin() {
.unwrap();
}
// Get the main state machine
let mut game_state_machine =
build_screen_state_machine().expect("Could not init state main state machine");
// Build the game context
let mut context = RefCell::new(GameContext::new());
let (mut rl, thread) = raylib::init()
.size(640, 480)
.title(&game_config.name)
@ -87,32 +102,44 @@ pub async fn game_begin() {
info!("Starting the render loop");
while !rl.window_should_close() {
// Profile the main game loop
puffin::profile_scope!("main_loop");
puffin::GlobalProfiler::lock().new_frame();
// Update the GPU texture that we draw to. This handles screen resizing and some other stuff
dynamic_texture.update(&mut rl, &thread).unwrap();
// Switch into draw mode
let mut d = rl.begin_drawing(&thread);
// Fetch the screen size once to work with in render code
let screen_size = Vector2::new(d.get_screen_width() as f32, d.get_screen_height() as f32);
// Update the pixel shader to correctly handle the screen size
pixel_shader.set_variable("viewport", screen_size).unwrap();
// Build render context
{
let render_ctx = RefCell::new((RefCell::new(d), context));
// Render the game via the pixel shader
render_to_texture(&mut dynamic_texture, || {
// Profile the internal render code
puffin::profile_scope!("internal_shaded_render");
d.clear_background(Color::WHITE);
d.draw_text("Hello, world!", 12, 12, 20, Color::BLACK);
let angle = (d.get_time() as f32 * 80.0).to_radians();
let screen_center = Vector2::new(
d.get_screen_width() as f32 / 2.0,
d.get_screen_height() as f32 / 2.0,
);
let top = rotate_vector(Vector2::new(0.0, -100.0), angle) + screen_center;
let right = rotate_vector(Vector2::new(100.0, 0.0), angle) + screen_center;
let left = rotate_vector(Vector2::new(-100.0, 0.0), angle) + screen_center;
// Run a state machine iteration
let result = game_state_machine.run(&render_ctx);
d.draw_triangle(top, left, right, Color::BLACK);
d.draw_fps(10, 100);
if let Err(err) = result {
error!("Main state machine encountered an error while running!");
error!("Main thread crash!!");
error!("Cannot recover from error");
panic!("{:?}", err);
}
});
}
// Send the texture to the GPU to be drawn
pixel_shader.process_texture_and_render(&mut d, &thread, &dynamic_texture);
}
}

View File

@ -0,0 +1,66 @@
use std::cell::RefCell;
use dirty_fsm::{Action, ActionFlag};
use raylib::{
color::Color,
prelude::{RaylibDraw, RaylibDrawHandle},
};
use tracing::{debug, error, info, trace};
use crate::{context::GameContext, gfx::render_layer::ScreenSpaceRender};
use super::{RenderContext, Scenes, ScreenError};
#[derive(Debug)]
pub struct FsmErrorScreen {}
impl FsmErrorScreen {
/// Construct a new FsmErrorScreen
pub fn new() -> Self {
Self {}
}
}
impl<Rl> Action<Scenes, ScreenError, RefCell<(RefCell<Rl>, RefCell<GameContext>)>> for FsmErrorScreen
where
Rl: RaylibDraw,
{
fn on_register(&mut self) -> Result<(), ScreenError> {
debug!("Registered");
Ok(())
}
fn on_first_run(&mut self, context: &RefCell<(RefCell<Rl>, RefCell<GameContext>)>) -> Result<(), ScreenError> {
debug!("Running FsmErrorScreen for the first time");
Ok(())
}
fn execute(
&mut self,
delta: &chrono::Duration,
context: &RefCell<(RefCell<Rl>, RefCell<GameContext>)>,
) -> Result<dirty_fsm::ActionFlag<Scenes>, ScreenError> {
trace!("execute() called on FsmErrorScreen, but we have not logic");
Ok(ActionFlag::Continue)
}
fn on_finish(&mut self, interrupted: bool) -> Result<(), ScreenError> {
debug!("Finished FsmErrorScreen");
Ok(())
}
}
impl ScreenSpaceRender for FsmErrorScreen {
fn render_screen_space(&self, raylib: &mut raylib::prelude::RaylibDrawHandle) {
raylib.clear_background(Color::RED);
// Render a warning message
raylib.draw_text(
"FSM Failure\nFalling back to Default state",
10,
10,
40,
Color::WHITE,
)
}
}

View File

@ -0,0 +1,28 @@
use dirty_fsm::Action;
use crate::context::GameContext;
use super::{Scenes, ScreenError};
#[derive(Debug)]
pub struct LoadingScreen {
}
impl Action<Scenes, ScreenError, GameContext> for LoadingScreen {
fn on_register(&mut self) -> Result<(), ScreenError> {
todo!()
}
fn on_first_run(&mut self, context: &mut GameContext) -> Result<(), ScreenError> {
todo!()
}
fn execute(&mut self, delta: &chrono::Duration, context: &mut GameContext) -> Result<dirty_fsm::ActionFlag<Scenes>, ScreenError> {
todo!()
}
fn on_finish(&mut self, interrupted: bool) -> Result<(), ScreenError> {
todo!()
}
}

37
game/src/scenes/mod.rs Normal file
View File

@ -0,0 +1,37 @@
use std::cell::{RefCell, RefMut};
use dirty_fsm::{Action, StateMachine};
use raylib::{RaylibHandle, prelude::{RaylibDraw, RaylibDrawHandle}};
use crate::{
context::GameContext,
gfx::render_layer::{FrameUpdate, ScreenSpaceRender, WorldSpaceRender},
};
use self::fsm_error_screen::FsmErrorScreen;
pub mod fsm_error_screen;
// pub mod loading_screen;
/// Data passed to all scenes upon render
pub type RenderContext<'a, 'b> = (&'b mut RaylibDrawHandle<'a>, &'b mut GameContext);
/// Defines all scenes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash)]
pub enum Scenes {
#[default]
FsmErrorScreen,
LoadingScreen,
}
/// Contains any possible errors thrown while rendering
#[derive(Debug, Error)]
pub enum ScreenError {}
/// Build the state machine for all scenes
pub fn build_screen_state_machine<Rl>(
) -> Result<StateMachine<Scenes, ScreenError, RefCell<(RefCell<Rl>, RefCell<GameContext>)>>, ScreenError> where Rl: RaylibDraw {
let mut machine = StateMachine::new();
machine.add_action(Scenes::FsmErrorScreen, FsmErrorScreen::new())?;
Ok(machine)
}

View File

@ -2,4 +2,4 @@ pub mod discord;
pub mod datastore;
pub mod game_config;
pub mod math;
pub mod statemachine;
pub mod shaders;

View File

@ -1,32 +0,0 @@
use std::{collections::HashMap, fmt::Display, hash::Hash};
pub struct StateMachine<State, Data>
where
State: Eq + Hash + Clone + Display + Default,
{
default_state: State,
callback_map: HashMap<State, Box<dyn Fn(&mut Data)>>,
}
impl<State, Data> StateMachine<State, Data>
where
State: Eq + Hash + Clone + Display + Default,
{
/// Construct a new StateMachine
pub fn new() -> Self {
Self {
default_state: State::default(),
callback_map: HashMap::new(),
}
}
/// Override the default state function
pub fn set_default_handler(&mut self, callback: Box<dyn Fn(&mut Data)>) {
self.callback_map.insert(self.default_state.clone(), callback);
}
/// Add a new state function
pub fn add_state(&mut self, state: State, callback: Box<dyn Fn(&mut Data)>) {
self.callback_map.insert(state, callback);
}
}