diff options
author | metamuffin <yvchraiqi@protonmail.com> | 2022-06-09 09:46:00 +0200 |
---|---|---|
committer | metamuffin <yvchraiqi@protonmail.com> | 2022-06-09 09:46:00 +0200 |
commit | 7a0d09e5cd0075e2a0d3db4505d7ec77dff35ae0 (patch) | |
tree | 5586745b7a9b871b31512cba9f964dabda4651f0 /renderer/src/main.rs | |
download | twclient-7a0d09e5cd0075e2a0d3db4505d7ec77dff35ae0.tar twclient-7a0d09e5cd0075e2a0d3db4505d7ec77dff35ae0.tar.bz2 twclient-7a0d09e5cd0075e2a0d3db4505d7ec77dff35ae0.tar.zst |
(reset git)
Diffstat (limited to 'renderer/src/main.rs')
-rw-r--r-- | renderer/src/main.rs | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/renderer/src/main.rs b/renderer/src/main.rs new file mode 100644 index 0000000..d763336 --- /dev/null +++ b/renderer/src/main.rs @@ -0,0 +1,254 @@ +pub mod map; + +use glutin::{ + event::{Event, KeyboardInput, VirtualKeyCode, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::{Window, WindowBuilder}, + ContextWrapper, GlProfile, PossiblyCurrent, +}; +use log::{error, info, warn}; +use map::MapRenderer; +use signal_hook::{ + consts::{SIGINT, SIGTERM}, + iterator::Signals, +}; +use skia_safe::{ + gpu::{gl::FramebufferInfo, BackendRenderTarget, SurfaceOrigin}, + Canvas, Color, Color4f, ColorSpace, ColorType, Paint, Point, Surface, +}; +use std::{ + convert::TryInto, net::IpAddr, process::exit, str::FromStr, sync::atomic::Ordering, thread, + time::Duration, +}; +use twclient::{ + client::{Client, ClientConfig, ClientInterface}, + world::World, + SHOULD_EXIT, +}; + +fn main() { + env_logger::init(); + + let event_loop = EventLoop::new(); + let wb = WindowBuilder::new().with_title("teeworlds"); + + let cb = glutin::ContextBuilder::new() + .with_depth_buffer(0) + .with_stencil_buffer(8) + .with_pixel_format(24, 8) + .with_gl_profile(GlProfile::Core); + + // TODO + // #[cfg(not(feature = "wayland"))] + // let cb = cb.with_double_buffer(Some(true)); + + let windowed_context = cb.build_windowed(wb, &event_loop).unwrap(); + + let windowed_context = unsafe { windowed_context.make_current().unwrap() }; + + gl::load_with(|s| windowed_context.get_proc_address(s)); + + let mut gr_context = skia_safe::gpu::DirectContext::new_gl(None, None).unwrap(); + + let fb_info = { + use gl::types::GLint; + let mut fboid: GLint = 0; + unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + + FramebufferInfo { + fboid: fboid.try_into().unwrap(), + format: skia_safe::gpu::gl::Format::RGBA8.into(), + } + }; + + windowed_context + .window() + .set_inner_size(glutin::dpi::Size::new(glutin::dpi::LogicalSize::new( + 1024.0, 1024.0, + ))); + + let surface = create_surface(&windowed_context, &fb_info, &mut gr_context); + + struct Env { + surface: Surface, + gr_context: skia_safe::gpu::DirectContext, + windowed_context: ContextWrapper<PossiblyCurrent, Window>, + } + + let mut env = Env { + surface, + gr_context, + windowed_context, + }; + + let mut args = std::env::args().skip(1); + let ip = IpAddr::from_str(args.next().unwrap().as_str()).unwrap(); + let port = u16::from_str(args.next().unwrap().as_str()).unwrap(); + let mut evloop = Client::new_evloop(); + let (client, client_interface) = Client::new( + &mut evloop, + ip, + port, + ClientConfig { + nick: "metamuffin".to_string(), + clan: "rustacean".to_string(), + timeout: "sdfhaiusdfhus".to_string(), + }, + ); + let mut network_thread = Some(std::thread::spawn(move || client.run(evloop))); + + let mut signals = Signals::new(&[SIGTERM, SIGINT]).unwrap(); + info!("setting up signal handlers"); + thread::spawn(move || { + for sig in signals.forever() { + warn!("received signal {:?}", sig); + SHOULD_EXIT.store(true, Ordering::Relaxed); + thread::sleep(Duration::from_secs(3)); + error!("exit timeout!"); + exit(1); + } + }); + + let mut renderer = Renderer { + client_interface, + map_renderer: MapRenderer::new(), + world: World::new(), + }; + + event_loop.run(move |event, _, control_flow| { + *control_flow = ControlFlow::Wait; + + #[allow(deprecated)] + match event { + Event::LoopDestroyed => {} + Event::RedrawEventsCleared => { + if SHOULD_EXIT.load(Ordering::Relaxed) { + warn!("waiting for networt thread to finish"); + network_thread.take().unwrap().join().unwrap(); + warn!("exiting renderer"); + exit(0); + } + renderer.tick(); + env.windowed_context.window().request_redraw(); + } + Event::WindowEvent { event, .. } => match event { + WindowEvent::Resized(physical_size) => { + env.surface = + create_surface(&env.windowed_context, &fb_info, &mut env.gr_context); + env.windowed_context.resize(physical_size) + } + WindowEvent::CloseRequested => { + warn!("renderer event loop stopped, telling the client to exit too"); + SHOULD_EXIT.store(true, Ordering::Relaxed); + *control_flow = ControlFlow::Exit + } + WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode, + modifiers, + .. + }, + .. + } => { + if modifiers.logo() { + if let Some(VirtualKeyCode::Q) = virtual_keycode { + *control_flow = ControlFlow::Exit; + } + } + env.windowed_context.window().request_redraw(); + } + _ => (), + }, + Event::RedrawRequested(_) => { + { + let dims = (env.surface.width() as f32, env.surface.height() as f32); + let canvas = env.surface.canvas(); + renderer.draw(canvas, dims) + } + env.surface.canvas().flush(); + env.windowed_context.swap_buffers().unwrap(); + } + _ => (), + } + }); +} + +pub struct Renderer { + client_interface: ClientInterface, + map_renderer: MapRenderer, + world: World, +} + +impl Renderer { + pub fn tick(&mut self) { + for m in self.client_interface.receive.try_iter() { + self.world.update(&m); + match m { + twclient::client::ClientMesgOut::MapChange { .. } => { + self.map_renderer.map_changed(&self.world) + } + _ => (), + } + } + } + pub fn draw(&mut self, canvas: &mut Canvas, dims: (f32, f32)) { + canvas.clear(Color::TRANSPARENT); + let center = self.world.local_tee().map(|t| (t.x, t.y)).unwrap_or((0, 0)); + + canvas.save(); + canvas.translate((dims.0 / 2.0, dims.1 / 2.0)); + canvas.translate((-center.0 as f32, -center.1 as f32)); + + self.map_renderer.draw(&self.world, canvas); + + let tee_paint = Paint::new( + Color4f { + a: 1.0, + r: 1.0, + g: 0.0, + b: 1.0, + }, + &ColorSpace::new_srgb(), + ); + for t in self.world.tees.values() { + canvas.draw_circle( + Point { + x: t.x as f32, + y: t.y as f32, + }, + 16.0, + &tee_paint, + ); + } + + canvas.restore(); + } +} + +fn create_surface( + windowed_context: &ContextWrapper<PossiblyCurrent, Window>, + fb_info: &FramebufferInfo, + gr_context: &mut skia_safe::gpu::DirectContext, +) -> skia_safe::Surface { + let pixel_format = windowed_context.get_pixel_format(); + let size = windowed_context.window().inner_size(); + let backend_render_target = BackendRenderTarget::new_gl( + ( + size.width.try_into().unwrap(), + size.height.try_into().unwrap(), + ), + pixel_format.multisampling.map(|s| s.try_into().unwrap()), + pixel_format.stencil_bits.try_into().unwrap(), + *fb_info, + ); + Surface::from_backend_render_target( + gr_context, + &backend_render_target, + SurfaceOrigin::BottomLeft, + ColorType::RGBA8888, + None, + None, + ) + .unwrap() +} |