aboutsummaryrefslogtreecommitdiff
path: root/renderer/src/main.rs
diff options
context:
space:
mode:
authormetamuffin <yvchraiqi@protonmail.com>2022-06-09 09:46:00 +0200
committermetamuffin <yvchraiqi@protonmail.com>2022-06-09 09:46:00 +0200
commit7a0d09e5cd0075e2a0d3db4505d7ec77dff35ae0 (patch)
tree5586745b7a9b871b31512cba9f964dabda4651f0 /renderer/src/main.rs
downloadtwclient-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.rs254
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()
+}