basic replayable replays
[carveJwlIkooP6JGAAIwe30JlM.git] / player_replay.c
1 #ifndef PLAYER_REPLAY_C
2 #define PLAYER_REPLAY_C
3
4 #include "player_replay.h"
5
6 VG_STATIC void local_replay_init( u32 bytes ){
7 localplayer.replay.data = vg_linear_alloc( vg_mem.rtmemory, bytes );
8 localplayer.replay.size = bytes;
9 localplayer.replay.cursor = -9999.9;
10 vg_console_reg_var( "k_replay_test", &k_replay_test, k_var_dtype_i32, 0 );
11 }
12
13 replay_gamestate *replay_frame_gamestate( replay_frame *frame, u16 index ){
14 void *baseptr = frame;
15
16 replay_gamestate *array = (baseptr + vg_align8( sizeof(replay_frame)));
17 return &array[ index ];
18 }
19
20 replay_sfx *replay_frame_sfx( replay_frame *frame, u16 index ){
21 void *gsarr = replay_frame_gamestate( frame, 0 );
22 u32 gssize = frame->gamestate_count * sizeof(replay_gamestate);
23
24 replay_sfx *array = (gsarr + vg_align8(gssize));
25 return &array[index];
26 }
27
28 u32 _replay_frame_size( u16 gamestate_count, u16 sfx_count ){
29 return vg_align8( sizeof( replay_frame ) ) +
30 vg_align8( gamestate_count * sizeof(replay_gamestate) ) +
31 vg_align8( sfx_count * sizeof(replay_sfx) );
32 }
33
34 u32 replay_frame_size( replay_frame *frame ){
35 return _replay_frame_size( frame->gamestate_count, frame->sfx_count );
36 }
37
38 VG_STATIC replay_frame *replay_newframe( replay_buffer *replay,
39 u16 gamestate_count, u16 sfx_count ){
40 replay_frame *frame = NULL;
41 if( replay->head ){
42 assert( replay->head );
43
44 u32 headsize = replay_frame_size( replay->head ),
45 nextpos = ((void *)replay->head - replay->data) + headsize,
46 nextsize = _replay_frame_size( gamestate_count, sfx_count );
47
48 if( nextsize > replay->size ){
49 vg_error( "Keyframe too big\n" );
50 return NULL;
51 }
52
53 if( nextpos + nextsize > replay->size )
54 nextpos = 0;
55
56 check_again:;
57
58 u32 tailpos = (void *)replay->tail - replay->data;
59
60 if( tailpos >= nextpos ){
61 if( nextpos + nextsize > tailpos ){
62 /* remove links */
63 if( replay->cursor_frame == replay->tail )
64 replay->cursor_frame = NULL;
65 if( replay->statehead == replay->tail )
66 replay->statehead = NULL;
67
68 /* pop node */
69 replay->tail = replay->tail->r;
70
71 if( replay->tail ) {
72 replay->tail->l = NULL;
73 goto check_again;
74 }
75 else
76 replay->head = NULL;
77 }
78 }
79
80 frame = replay->data + nextpos;
81
82 if( replay->head )
83 replay->head->r = frame;
84 }
85 else
86 frame = replay->data;
87
88 frame->gamestate_count = gamestate_count;
89 frame->sfx_count = sfx_count;
90 frame->l = replay->head;
91 frame->r = NULL;
92 replay->head = frame;
93 if( !replay->tail ) replay->tail = frame;
94 if( gamestate_count ) replay->statehead = frame;
95
96 return frame;
97 }
98
99 VG_STATIC void replay_seek( replay_buffer *replay, f64 t ){
100 if( !replay->head ) return;
101 assert( replay->tail );
102
103 if( t < replay->tail->time ) t = replay->tail->time;
104 if( t > replay->head->time ) t = replay->head->time;
105
106 if( !replay->cursor_frame ) {
107 replay->cursor = replay->head->time;
108 replay->cursor_frame = replay->head;
109
110 if( fabs(replay->head->time-t) > fabs(replay->tail->time-t) ){
111 replay->cursor = replay->tail->time;
112 replay->cursor_frame = replay->tail;
113 }
114 }
115
116 f64 dir = t - replay->cursor;
117 if( dir == 0.0 ) return;
118 dir = vg_signf( dir );
119
120 u32 i=0;
121 for( ; i<1024; i++ ){
122 if( dir < 0.0 )
123 if( t > replay->cursor_frame->time ) break;
124
125 replay_frame *next;
126 if( dir > 0.0 ) next = replay->cursor_frame->r;
127 else next = replay->cursor_frame->l;
128
129 if( !next ) break;
130
131 if( dir > 0.0 )
132 if( t < next->time ) break;
133
134 replay->cursor_frame = next;
135 replay->cursor = next->time;
136
137 if( i == 1023 ) return;
138 }
139
140 replay->cursor = t;
141 }
142
143 VG_STATIC replay_frame *replay_find_recent_stateframe( replay_buffer *replay ){
144 replay_frame *frame = replay->cursor_frame;
145
146 u32 i=0;
147 for( ; i<4096; i++ ){
148 if( !frame ) return frame;
149 if( frame->gamestate_count ) return frame;
150 frame = frame->l;
151 }
152
153 return NULL;
154 }
155
156 VG_STATIC void replay_debug_info( player_instance *player ){
157 player__debugtext( 2, "replay info" );
158
159 replay_buffer *replay = &player->replay;
160
161 u32 head = 0,
162 tail = 0;
163 if( replay->tail ) tail = (void *)replay->tail - replay->data;
164 if( replay->head ) head = (void *)replay->head - replay->data;
165
166 player__debugtext( 1, "head @%u | tail @%u\n", head, tail );
167
168 if( replay->statehead ){
169 u32 state = (void *)replay->statehead - replay->data;
170 player__debugtext( 1, "gs @%u\n", state );
171 }
172 else
173 player__debugtext( 1, "gs @NULL\n" );
174
175 f64 start = replay->cursor,
176 end = replay->cursor;
177 if( replay->tail ) start = replay->tail->time;
178 if( replay->head ) end = replay->head->time;
179
180 f64 cur = replay->cursor - start,
181 len = end - start;
182
183 player__debugtext( 1, "cursor: %.2fs / %.2fs\n", cur, len );
184 }
185
186 VG_STATIC void replay_imgui( player_instance *player ){
187 if( !k_replay_test ) return;
188
189 replay_buffer *replay = &player->replay;
190 f64 start = replay->cursor,
191 end = replay->cursor;
192 if( replay->tail ) start = replay->tail->time;
193 if( replay->head ) end = replay->head->time;
194 f64 len = end - start,
195 cur = (replay->cursor - start) / len;
196
197 char buffer[ 128 ];
198
199 /* mainbar */
200 ui_px height = 20,
201 cwidth = 2;
202 ui_rect bar = { 0, vg.window_y - height, vg.window_x, height };
203 ui_fill( bar, ui_colour( k_ui_bg ) );
204
205 /* cursor frame block */
206 if( replay->cursor_frame ){
207 if( replay->cursor_frame->r ){
208 f64 l = (replay->cursor_frame->r->time-replay->cursor_frame->time)/len,
209 s = (replay->cursor_frame->time - start) / len;
210 ui_rect box = { s*(f64)vg.window_x, bar[1]-2,
211 VG_MAX(4,(ui_px)l), bar[3]+2 };
212 ui_fill( box, ui_colour( k_ui_bg+4 ) );
213 }
214 }
215
216 /* cursor */
217 ui_rect cusor = { cur * (f64)vg.window_x - (cwidth/2), bar[1],
218 cwidth, bar[3] };
219 ui_fill( cusor, ui_colour( k_ui_bg+7 ) );
220
221 /* latest state marker */
222 if( replay->statehead ){
223 f64 t = (replay->statehead->time - start) / len;
224 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
225 ui_fill( tag, ui_colour( k_ui_green+k_ui_brighter ) );
226 }
227
228 /* previous state marker */
229 replay_frame *prev = replay_find_recent_stateframe( replay );
230 if( prev ){
231 f64 t = (prev->time - start) / len;
232 ui_rect tag = { t*(f64)vg.window_x, bar[1]-8, 2, bar[3]+8 };
233 ui_fill( tag, ui_colour( k_ui_yellow+k_ui_brighter ) );
234 }
235
236 cusor[1] -= height;
237 cusor[2] = 200;
238 snprintf( buffer, 128, "-%.2fs\n", (end-replay->cursor) );
239 ui_text( cusor, buffer, 1, k_ui_align_middle_left, 0 );
240
241 snprintf( buffer, 128, "-%.2fs\n", len );
242 ui_text( bar, buffer, 1, k_ui_align_middle_left, 0 );
243 ui_text( bar, "0s", 1, k_ui_align_middle_right, 0 );
244 }
245
246 #endif /* PLAYER_REPLAY_C */