]> git.localhorst.tv Git - blank.git/blob - src/app/app.cpp
try to get every chunk change saved to disk
[blank.git] / src / app / app.cpp
1 #include "Application.hpp"
2 #include "Assets.hpp"
3 #include "Environment.hpp"
4 #include "FrameCounter.hpp"
5 #include "State.hpp"
6 #include "StateControl.hpp"
7
8 #include "init.hpp"
9 #include "../audio/Sound.hpp"
10 #include "../graphics/ArrayTexture.hpp"
11 #include "../graphics/Font.hpp"
12 #include "../graphics/Texture.hpp"
13 #include "../world/BlockType.hpp"
14 #include "../world/Entity.hpp"
15
16 #include <iostream>
17 #include <stdexcept>
18 #include <SDL_image.h>
19
20 using std::string;
21
22
23 namespace blank {
24
25 Application::Application(Environment &e)
26 : env(e)
27 , states() {
28
29 }
30
31 Application::~Application() {
32         env.audio.StopAll();
33 }
34
35
36 void Application::RunN(size_t n) {
37         Uint32 last = SDL_GetTicks();
38         for (size_t i = 0; HasState() && i < n; ++i) {
39                 Uint32 now = SDL_GetTicks();
40                 int delta = now - last;
41                 Loop(delta);
42                 last = now;
43         }
44 }
45
46 void Application::RunT(size_t t) {
47         Uint32 last = SDL_GetTicks();
48         Uint32 finish = last + t;
49         while (HasState() && last < finish) {
50                 Uint32 now = SDL_GetTicks();
51                 int delta = now - last;
52                 Loop(delta);
53                 last = now;
54         }
55 }
56
57 void Application::RunS(size_t n, size_t t) {
58         for (size_t i = 0; HasState() && i < n; ++i) {
59                 Loop(t);
60         }
61 }
62
63
64 void Application::Run() {
65         Uint32 last = SDL_GetTicks();
66         env.window.GrabMouse();
67         while (HasState()) {
68                 Uint32 now = SDL_GetTicks();
69                 int delta = now - last;
70                 Loop(delta);
71                 last = now;
72         }
73 }
74
75 void Application::Loop(int dt) {
76         env.counter.EnterFrame();
77         HandleEvents();
78         if (!HasState()) return;
79         Update(dt);
80         env.state.Commit(*this);
81         if (!HasState()) return;
82         Render();
83         env.counter.ExitFrame();
84 }
85
86
87 void Application::HandleEvents() {
88         env.counter.EnterHandle();
89         SDL_Event event;
90         while (HasState() && SDL_PollEvent(&event)) {
91                 Handle(event);
92                 env.state.Commit(*this);
93         }
94         env.counter.ExitHandle();
95 }
96
97 void Application::Handle(const SDL_Event &event) {
98         switch (event.type) {
99                 case SDL_WINDOWEVENT:
100                         Handle(event.window);
101                         break;
102                 default:
103                         GetState().Handle(event);
104                         break;
105         }
106 }
107
108 void Application::Handle(const SDL_WindowEvent &event) {
109         switch (event.event) {
110                 case SDL_WINDOWEVENT_FOCUS_GAINED:
111                         env.window.GrabMouse();
112                         break;
113                 case SDL_WINDOWEVENT_FOCUS_LOST:
114                         env.window.ReleaseMouse();
115                         break;
116                 case SDL_WINDOWEVENT_RESIZED:
117                         env.viewport.Resize(event.data1, event.data2);
118                         break;
119                 default:
120                         break;
121         }
122 }
123
124 void Application::Update(int dt) {
125         env.counter.EnterUpdate();
126         if (HasState()) {
127                 GetState().Update(dt);
128         }
129         env.counter.ExitUpdate();
130 }
131
132 void Application::Render() {
133         // gl implementation may (and will probably) delay vsync blocking until
134         // the first write after flipping, which is this clear call
135         env.viewport.Clear();
136         env.counter.EnterRender();
137
138         if (HasState()) {
139                 GetState().Render(env.viewport);
140         }
141
142         env.counter.ExitRender();
143         env.window.Flip();
144 }
145
146
147 void Application::PushState(State *s) {
148         states.emplace(s);
149 }
150
151 State *Application::PopState() {
152         State *s = states.top();
153         states.pop();
154         return s;
155 }
156
157 State *Application::SwitchState(State *s_new) {
158         State *s_old = states.top();
159         states.top() = s_new;
160         return s_old;
161 }
162
163 State &Application::GetState() {
164         return *states.top();
165 }
166
167 bool Application::HasState() const noexcept {
168         return !states.empty();
169 }
170
171
172 void StateControl::Commit(Application &app) {
173         while (!cue.empty()) {
174                 Memo m(cue.front());
175                 cue.pop();
176                 switch (m.cmd) {
177                         case PUSH:
178                                 app.PushState(m.state);
179                                 break;
180                         case SWITCH:
181                                 app.SwitchState(m.state);
182                                 break;
183                         case POP:
184                                 app.PopState();
185                                 break;
186                         case POP_ALL:
187                                 while (app.HasState()) {
188                                         app.PopState();
189                                 }
190                                 break;
191                 }
192         }
193 }
194
195
196 Assets::Assets(const string &base)
197 : fonts(base + "fonts/")
198 , sounds(base + "sounds/")
199 , textures(base + "textures/") {
200
201 }
202
203 Font Assets::LoadFont(const string &name, int size) const {
204         string full = fonts + name + ".ttf";
205         return Font(full.c_str(), size);
206 }
207
208 Sound Assets::LoadSound(const string &name) const {
209         string full = sounds + name + ".wav";
210         return Sound(full.c_str());
211 }
212
213 Texture Assets::LoadTexture(const string &name) const {
214         string full = textures + name + ".png";
215         Texture tex;
216         SDL_Surface *srf = IMG_Load(full.c_str());
217         if (!srf) {
218                 throw SDLError("IMG_Load");
219         }
220         tex.Bind();
221         tex.Data(*srf);
222         SDL_FreeSurface(srf);
223         return tex;
224 }
225
226 void Assets::LoadTexture(const string &name, ArrayTexture &tex, int layer) const {
227         string full = textures + name + ".png";
228         SDL_Surface *srf = IMG_Load(full.c_str());
229         if (!srf) {
230                 throw SDLError("IMG_Load");
231         }
232         tex.Bind();
233         try {
234                 tex.Data(layer, *srf);
235         } catch (...) {
236                 SDL_FreeSurface(srf);
237                 throw;
238         }
239         SDL_FreeSurface(srf);
240 }
241
242
243 void FrameCounter::EnterFrame() noexcept {
244         last_enter = SDL_GetTicks();
245         last_tick = last_enter;
246 }
247
248 void FrameCounter::EnterHandle() noexcept {
249         Tick();
250 }
251
252 void FrameCounter::ExitHandle() noexcept {
253         current.handle = Tick();
254 }
255
256 void FrameCounter::EnterUpdate() noexcept {
257         Tick();
258 }
259
260 void FrameCounter::ExitUpdate() noexcept {
261         current.update = Tick();
262 }
263
264 void FrameCounter::EnterRender() noexcept {
265         Tick();
266 }
267
268 void FrameCounter::ExitRender() noexcept {
269         current.render = Tick();
270 }
271
272 void FrameCounter::ExitFrame() noexcept {
273         Uint32 now = SDL_GetTicks();
274         current.total = now - last_enter;
275         current.running = current.handle + current.update + current.render;
276         current.waiting = current.total - current.running;
277         Accumulate();
278
279         ++cur_frame;
280         if (cur_frame >= NUM_FRAMES) {
281                 Push();
282                 cur_frame = 0;
283                 changed = true;
284         } else {
285                 changed = false;
286         }
287 }
288
289 int FrameCounter::Tick() noexcept {
290         Uint32 now = SDL_GetTicks();
291         int delta = now - last_tick;
292         last_tick = now;
293         return delta;
294 }
295
296 void FrameCounter::Accumulate() noexcept {
297         sum.handle += current.handle;
298         sum.update += current.update;
299         sum.render += current.render;
300         sum.running += current.running;
301         sum.waiting += current.waiting;
302         sum.total += current.total;
303
304         max.handle = std::max(current.handle, max.handle);
305         max.update = std::max(current.update, max.update);
306         max.render = std::max(current.render, max.render);
307         max.running = std::max(current.running, max.running);
308         max.waiting = std::max(current.waiting, max.waiting);
309         max.total = std::max(current.total, max.total);
310
311         current = Frame<int>();
312 }
313
314 void FrameCounter::Push() noexcept {
315         peak = max;
316         avg.handle = sum.handle * factor;
317         avg.update = sum.update * factor;
318         avg.render = sum.render * factor;
319         avg.running = sum.running * factor;
320         avg.waiting = sum.waiting * factor;
321         avg.total = sum.total * factor;
322
323         sum = Frame<int>();
324         max = Frame<int>();
325 }
326
327 }