1use gilrs::Gilrs;
61use optic_core::{log_error, CamProj, Coord2D, OpticResult, Size2D, CRIMSON};
62use optic_core::{end, end_success, ERROR, SUCCESS};
63use optic_render::{Camera, GPU};
64use optic_window::{Events, Window};
65use winit::application::ApplicationHandler;
66use winit::event::WindowEvent;
67use winit::event_loop::{ActiveEventLoop, EventLoop};
68use winit::window::WindowId;
69
70#[cfg(feature = "online")]
71use optic_online::NetworkHandle;
72
73use crate::{Runtime, Time};
74
75pub struct Game {
81 pub renderer: GPU,
82 pub camera: Camera,
83 pub events: Events,
84 pub time: Time,
85 pub window: Window,
86
87 event_loop: Option<EventLoop<()>>,
88 surface_index: usize,
89 gilrs: Gilrs,
90 runtime: Option<Box<dyn Runtime>>,
91 running: bool,
92 started: bool,
93 requested_size: Size2D,
94 resized_once: bool,
95
96 #[cfg(feature = "online")]
97 pub(crate) network: Option<NetworkHandle>,
98}
99
100impl Game {
101 pub fn new<R: Runtime + 'static>(runtime: R) -> OpticResult<Game> {
119 let size = Size2D::from(500,500);
120 let bg_color = CRIMSON;
121 let title = "OPTIC GAME";
122 let el = EventLoop::builder()
123 .build()
124 .map_err(|e| optic_core::OpticError::custom(&format!("event loop creation failed: {e}")))?;
125 let window = Window::new(&el, title, size);
126 let actual_size = window.size();
127 let handle = window.raw_handle()
128 .ok_or_else(|| optic_core::OpticError::custom("failed to get raw window handle"))?;
129 let display_handle = window.raw_display_handle()
130 .ok_or_else(|| optic_core::OpticError::custom("failed to get raw display handle"))?;
131
132 let mut gpu = GPU::new_windowed(handle, display_handle, actual_size)?;
133 gpu.ctx.set_vsync(true);
134 gpu.canvas_size = actual_size;
135 gpu.set_bg_color(bg_color);
136 let surface_index = 0;
137
138 let gilrs = Gilrs::new()
139 .map_err(|e| optic_core::OpticError::custom(&format!("gilrs init failed: {e}")))?;
140 Ok(Game {
141 renderer: gpu,
142 camera: Camera::new(size, CamProj::Persp),
143 events: Events::new(),
144 time: Time::new(),
145 event_loop: Some(el),
146 window,
147 surface_index,
148 gilrs,
149 runtime: Some(Box::new(runtime)),
150 running: true,
151 started: false,
152 requested_size: size,
153 resized_once: false,
154 #[cfg(feature = "online")]
155 network: None,
156 })
157 }
158
159 pub fn run<R: Runtime + 'static>(runtime: R) {
170 match Game::new(runtime) {
171 Ok(game) => {
172 game.start();
173 end(SUCCESS);
174 }
175 Err(e) => {
176 log_error!("{}", e);
177 end(ERROR);
178 }
179 }
180 }
181
182 fn start(mut self) {
183 let el = self.event_loop.take().unwrap();
184 let _ = el.run_app(&mut self);
185 }
186
187 pub fn exit(&mut self) {
201 self.running = false;
202 }
203
204 #[cfg(feature = "online")]
206 pub fn network(&self) -> Option<&NetworkHandle> {
207 self.network.as_ref()
208 }
209
210 #[cfg(feature = "online")]
212 pub fn network_mut(&mut self) -> Option<&mut NetworkHandle> {
213 self.network.as_mut()
214 }
215
216 #[cfg(feature = "online")]
225 pub fn enable_networking(&mut self, config: optic_core::NetworkConfig) -> OpticResult<()> {
226 let handle = NetworkHandle::new(config)?;
227 self.network = Some(handle);
228 Ok(())
229 }
230
231}
232
233impl ApplicationHandler for Game {
234 fn resumed(&mut self, _el: &ActiveEventLoop) {
235 self.time.start_time = std::time::Instant::now();
236 self.time.prev_time = std::time::Instant::now();
237 self.time.prev_sec = std::time::Instant::now();
238 }
239
240 fn window_event(
241 &mut self,
242 _el: &ActiveEventLoop,
243 id: WindowId,
244 event: WindowEvent,
245 ) {
246 if !self.window.is_running() { return; }
247 if self.window.id().unwrap() != id { return; }
248
249 match &event {
250 WindowEvent::Resized(_size) => {
251 self.renderer.ctx.resize_window(self.surface_index, self.window.size());
252 let _ = self.renderer.ctx.make_current(self.surface_index);
253 self.renderer.canvas_size = self.window.size();
254 self.camera.set_size(self.window.size());
255 if !self.resized_once && (_size.width != self.requested_size.w || _size.height != self.requested_size.h) {
256 self.resized_once = true;
257 self.window.set_size(self.requested_size);
258 }
259 }
260 WindowEvent::CursorMoved { position, .. } => {
261 self.window.notify_cursor_moved(Coord2D::from(position.x, position.y));
262 }
263 WindowEvent::CursorEntered { .. } => {
264 self.window.notify_cursor_inside(true);
265 }
266 WindowEvent::CursorLeft { .. } => {
267 self.window.notify_cursor_inside(false);
268 }
269 WindowEvent::CloseRequested => {
270 self.events.close_requested = true;
271 }
272 _ => {}
273 }
274 self.events.process_window_event(&event, &self.window);
275 }
276
277 fn about_to_wait(&mut self, el: &ActiveEventLoop) {
278 if !self.running || self.window.is_closed() {
279 #[cfg(feature = "online")]
280 if let Some(mut net) = self.network.take() {
281 net.shutdown();
282 }
283 if let Some(mut runtime) = self.runtime.take() {
284 runtime.end(self);
285 }
286 end_success();
287 el.exit();
288 return;
289 }
290
291 while let Some(gilrs_event) = self.gilrs.next_event() {
292 self.events.process_gilrs_event(&gilrs_event);
293 }
294
295 #[cfg(feature = "online")]
296 if let Some(net) = &mut self.network {
297 net.poll(&mut self.events.network);
298 }
299
300 let _ = self.renderer.ctx.make_current(self.surface_index);
301 self.renderer.clear();
302 self.time.update();
303
304 self.window.update_frame();
305
306 self.camera.pre_update();
307
308 let mut runtime = self.runtime.take().unwrap();
309 if !self.started {
310 runtime.start(self);
311 self.started = true;
312 self.window.set_visible(true);
313 self.window.center_on_screen();
314 }
315 runtime.update(self);
316 self.runtime = Some(runtime);
317
318 let _ = self.renderer.ctx.swap_buffers(self.surface_index);
319 self.events.end_frame();
320 self.window.request_redraw();
321 }
322}