1 /*
2 Copyright (C) 2003-2006 Andrey Nazarov
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 */
20
21 //
22 // mvd_client.c
23 //
24
25 #include "server.h"
26 #include "mvd.h"
27 #include "version.h"
28
29 mvd_t mvd;
30
31 // FIXME: these are used only for translating client_t into mvdClient_t
32 edict_t mvd_edicts[MAX_CLIENTS + 1];
33
34 cmdbuf_t mvd_buffer;
35 byte mvd_buffer_text[MAX_STRING_CHARS];
36
37 cvar_t *mvd_buffer_size;
38 cvar_t *mvd_pause;
39
40 void MVD_DPrintf( const char *fmt, ... )
41 __attribute__(( format( printf, 1, 2 ) ));
42
MVD_DPrintf(const char * fmt,...)43 void MVD_DPrintf( const char *fmt, ... ) {
44 va_list argptr;
45 char text[MAXPRINTMSG];
46
47 if( !mvd_debug->integer ) {
48 return;
49 }
50
51 va_start( argptr, fmt );
52 Q_vsnprintf( text, sizeof( text ), fmt, argptr );
53 va_end( argptr );
54
55 Com_Printf( S_COLOR_BLUE "%s", text );
56 }
57
MVD_ClientCommand(const char * string)58 void MVD_ClientCommand( const char *string ) {
59 if( !mvd.netchan ) {
60 return;
61 }
62 MSG_WriteByte( clc_stringcmd );
63 MSG_WriteString( string );
64 MSG_FlushTo( &mvd.netchan->message );
65 }
66
MVD_ForwardToServer_f(void)67 void MVD_ForwardToServer_f( void ) {
68 if( mvd.state < MVD_CONNECTED ) {
69 Com_Printf( "Can't \"%s\", not connected\n", Cmd_Argv( 0 ) );
70 return;
71 }
72
73 if( mvd.demoplayback ) {
74 return;
75 }
76
77 // don't forward the first argument
78 if( Cmd_Argc() > 1 ) {
79 MVD_ClientCommand( Cmd_RawArgs() );
80 }
81 }
82
83 /* called initially at ClientBegin */
MVD_GameSetValidPos(mvdClient_t * client)84 static void MVD_GameSetValidPos( mvdClient_t *client ) {
85 mvdFrame_t *frame;
86 player_state_t *ps;
87 entityStateEx_t *ent;
88 mvdGamestate_t *gs;
89 int i, j;
90
91 ps = &client->ps;
92 memset( ps, 0, sizeof( *ps ) );
93
94 if( client->admin ) {
95 gs = &mvd.gamestates[mvd.gamestateSequence & GAMESTATE_MASK];
96 } else {
97 gs = &mvd.gamestates[mvd.activeGamestateSequence & GAMESTATE_MASK];
98 }
99
100 if( gs->spawnSet ) {
101 VectorScale( gs->spawnOrigin, 8, ps->pmove.origin );
102 VectorCopy( gs->spawnAngles, ps->viewangles );
103 } else {
104 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
105 for( i = 0; i < frame->numEntityStates; i++ ) {
106 j = ( frame->firstEntityState + i ) % mvd.maxEntityStates;
107 ent = &mvd.entityStates[j];
108 if( ent->s.solid != 31 ) {
109 VectorScale( ent->s.origin, 8, ps->pmove.origin );
110 break;
111 }
112 }
113 if( i == frame->numEntityStates ) {
114 Com_WPrintf( "Don't know where to spawn %s\n", client->cl->name );
115 }
116 }
117
118 ps->viewangles[ROLL] = 0;
119 for( i = 0; i < 3; i++ ) {
120 ps->pmove.delta_angles[i] = ANGLE2SHORT( ps->viewangles[i] ) -
121 client->lastcmd.angles[i];
122 }
123
124 ps->fov = client->fov;
125 ps->pmove.pm_flags = client->pmflags;
126 ps->pmove.pm_type = PM_SPECTATOR;
127
128 client->clientNum = CLIENTNUM_NONE;
129 client->savedClientNum = CLIENTNUM_NONE;
130 client->followClientNum = CLIENTNUM_NONE;
131 client->following = qfalse;
132 }
133
MVD_GameStartObserving(mvdClient_t * client)134 static void MVD_GameStartObserving( mvdClient_t *client ) {
135 player_state_t *ps;
136 mvdPlayer_t *oldplayer;
137 int i, length;
138 int savedLayouts;
139
140 ps = &client->ps;
141 ps->viewangles[ROLL] = 0;
142 for( i = 0; i < 3; i++ ) {
143 ps->pmove.delta_angles[i] = ANGLE2SHORT( ps->viewangles[i] ) -
144 client->lastcmd.angles[i];
145 }
146
147 VectorClear( ps->kick_angles );
148
149 ps->fov = client->fov;
150 ps->blend[0] = 0;
151 ps->blend[1] = 0;
152 ps->blend[2] = 0;
153 ps->blend[3] = 0;
154 ps->pmove.pm_flags = client->pmflags;
155 ps->pmove.pm_type = PM_SPECTATOR;
156 ps->rdflags = 0;
157 ps->gunindex = 0;
158
159 if( client->scoreboard <= SBOARD_FOLLOW ) {
160 savedLayouts = 0;
161 } else {
162 savedLayouts = ps->stats[STAT_LAYOUTS];
163 }
164 for( i = 0; i < MAX_STATS; i++ ) {
165 ps->stats[i] = 0;
166 }
167 ps->stats[STAT_HEALTH] = 100;
168 ps->stats[STAT_LAYOUTS] = savedLayouts;
169
170 if( client->following && client->followClientNum != CLIENTNUM_NONE ) {
171 oldplayer = &mvd.players[client->followClientNum];
172 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
173 if( oldplayer->configstrings[i] ) {
174 MSG_WriteByte( svc_configstring );
175 MSG_WriteShort( i );
176 length = strlen( sv.configstrings[i] );
177 if( length > MAX_QPATH ) {
178 length = MAX_QPATH;
179 }
180 MSG_WriteData( sv.configstrings[i], length );
181 MSG_WriteByte( 0 );
182 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
183 }
184 }
185 }
186
187 client->clientNum = CLIENTNUM_NONE;
188 client->savedClientNum = client->followClientNum;
189 client->followClientNum = CLIENTNUM_NONE;
190 client->following = qfalse;
191 client->savedFollowing = qfalse;
192
193 SV_ClientPrintf( client->cl, PRINT_MEDIUM,
194 "[MVD] Switched to freefloat mode.\n" );
195 }
196
MVD_UpdateFollower(mvdClient_t * client,playerStateEx_t * src)197 void MVD_UpdateFollower( mvdClient_t *client, playerStateEx_t *src ) {
198 player_state_t *ps = &client->ps;
199
200 *ps = src->ps;
201 if( client->cl->protocol == PROTOCOL_VERSION_Q2PRO ) {
202 if( client->cl->settings[CLS_LOCALFOV] ) {
203 ps->fov = client->fov;
204 }
205 } else if( mvd_custom_fov->integer ) {
206 ps->fov = client->fov;
207 }
208 ps->pmove.pm_flags &= ~PMF_TELEPORT_BIT;
209 ps->pmove.pm_flags |= client->pmflags | PMF_NO_PREDICTION;
210 ps->pmove.pm_type = PM_FREEZE;
211 if( !client->admin ) {
212 if( client->scoreboard > SBOARD_FOLLOW ) {
213 ps->stats[STAT_LAYOUTS] = 1;
214 } else if( client->scoreboard == SBOARD_NONE ) {
215 ps->stats[STAT_LAYOUTS] = 0;
216 }
217 }
218 client->clientNum = src->clientNum;
219 }
220
MVD_UpdateLayoutClients(mvdClient_t * client)221 static void MVD_UpdateLayoutClients( mvdClient_t *client ) {
222 char layout[MAX_STRING_CHARS];
223 char buffer[MAX_STRING_CHARS];
224 char status[MAX_QPATH];
225 static const char *header =
226 "xl 32 yb -40 string2 \""APPLICATION" "VERSION"\" "
227 "yb -32 string http://q2pro.sf.net "
228 "xv 0 yv 0 string2 Name "
229 "xv 152 string2 Ping "
230 "xv 208 string2 Status ";
231 int length, totalLength;
232 client_t *cl;
233 mvdClient_t *mvdcl;
234 char name[MAX_CLIENT_NAME];
235 char *s;
236 int y;
237
238 strcpy( layout, header );
239 totalLength = strlen( layout );
240
241 y = 8;
242 FOR_EACH_CLIENT( cl ) {
243 if( cl->state < cs_connected ) {
244 continue;
245 }
246 if( cl->protocol == PROTOCOL_VERSION_MVD ) {
247 continue;
248 }
249 if( cl->state == cs_spawned ) {
250 mvdcl = ( mvdClient_t * )cl->edict->client;
251 if( mvdcl->following ) {
252 if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
253 s = sv.configstrings[CS_PLAYERSKINS + mvdcl->followClientNum];
254 Q_strncpyz( name, s, sizeof( name ) );
255 s = strchr( name, '\\' );
256 if( s ) {
257 *s = 0;
258 }
259 Com_sprintf( status, sizeof( status ), "-> %s", name );
260 } else {
261 strcpy( status, "following" );
262 }
263 } else {
264 strcpy( status, "observing" );
265 }
266 } else {
267 strcpy( status, "connecting" );
268 }
269 length = Com_sprintf( buffer, sizeof( buffer ),
270 "xv 0 yv %d string \"%.16s\" "
271 "xv 152 string %d "
272 "xv 208 string \"%s\" ",
273 y, cl->name, cl->ping, status );
274 if( totalLength + length > sizeof( layout ) - 1 ) {
275 break;
276 }
277 strcpy( layout + totalLength, buffer );
278 totalLength += length;
279 y += 8;
280 }
281
282 MSG_WriteByte( svc_layout );
283 MSG_WriteString( layout );
284
285 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
286
287 client->ps.stats[STAT_LAYOUTS] = 1;
288 client->layoutTime = sv.time + LAYOUT_MSEC;
289 }
290
MVD_UpdateLayoutScores(mvdClient_t * client)291 static void MVD_UpdateLayoutScores( mvdClient_t *client ) {
292 mvdPlayer_t *player;
293
294 if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
295 if( mvd.clientNum == CLIENTNUM_NONE ) {
296 client->ps.stats[STAT_LAYOUTS] = 1;
297 return;
298 }
299 player = &mvd.players[mvd.clientNum];
300 } else {
301 player = &mvd.players[0];
302 }
303 MSG_WriteByte( svc_layout );
304 MSG_WriteString( player->layout );
305 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
306
307 client->ps.stats[STAT_LAYOUTS] = 1;
308 }
309
MVD_SetDefaultLayout(mvdClient_t * client)310 static void MVD_SetDefaultLayout( mvdClient_t *client ) {
311 /*if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
312 client->scoreboard = SBOARD_FOLLOW;
313 MVD_UpdateLayoutFollow( client );
314 } else*/ {
315 client->ps.stats[STAT_LAYOUTS] = 0;
316 client->scoreboard = SBOARD_NONE;
317 }
318 }
319
MVD_GameStartFollowing(mvdClient_t * client,int playerNum)320 static void MVD_GameStartFollowing( mvdClient_t *client, int playerNum ) {
321 mvdPlayer_t *player, *oldplayer;
322 int i, j, length;
323 char name[MAX_CLIENT_NAME];
324 char *s;
325 mvdFrame_t *frame;
326 playerStateEx_t *ps = NULL;
327
328 if( client->admin ) {
329 frame = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
330 } else {
331 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
332 }
333
334 if( !frame->numPlayerStates ) {
335 SV_ClientPrintf( client->cl, PRINT_MEDIUM,
336 "[MVD] No players to follow.\n" );
337 if( client->following ) {
338 MVD_GameStartObserving( client );
339 }
340 return;
341 }
342
343 // find the desired player in current frame
344 for( i = 0; i < frame->numPlayerStates; i++ ) {
345 j = ( frame->firstPlayerState + i ) % mvd.maxPlayerStates;
346 ps = &mvd.playerStates[j];
347 if( ps->number == playerNum ) {
348 break;
349 }
350 }
351
352 if( i == frame->numPlayerStates ) {
353 // pick up the first player
354 if( playerNum != CLIENTNUM_NONE ) {
355 SV_ClientPrintf( client->cl, PRINT_MEDIUM,
356 "[MVD] Player %d is not active.\n", playerNum );
357 }
358 j = frame->firstPlayerState % mvd.maxPlayerStates;
359 ps = &mvd.playerStates[j];
360 playerNum = ps->number;
361 }
362
363 if( client->following && playerNum == client->followClientNum ) {
364 return;
365 }
366
367 player = &mvd.players[playerNum];
368 oldplayer = NULL;
369 if( client->following && client->followClientNum != CLIENTNUM_NONE ) {
370 oldplayer = &mvd.players[client->followClientNum];
371 }
372
373 client->savedClientNum = client->followClientNum;
374 client->followClientNum = playerNum;
375 client->following = qtrue;
376
377 client->pmflags ^= PMF_TELEPORT_BIT;
378 MVD_UpdateFollower( client, ps );
379
380 if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
381 s = sv.configstrings[CS_PLAYERSKINS + playerNum];
382 Q_strncpyz( name, s, sizeof( name ) );
383 s = strchr( name, '\\' );
384 if( s ) {
385 *s = 0;
386 }
387 } else {
388 strcpy( name, "the camera" );
389 }
390
391 SV_ClientPrintf( client->cl, PRINT_MEDIUM, "[MVD] Following %s.\n", name );
392
393 // send delta configstrings
394 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
395 if( player->configstrings[i] ) {
396 MSG_WriteByte( svc_configstring );
397 MSG_WriteShort( i );
398 MSG_WriteString( player->configstrings[i] );
399 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
400 } else if( oldplayer && oldplayer->configstrings[i] ) {
401 MSG_WriteByte( svc_configstring );
402 MSG_WriteShort( i );
403 length = strlen( sv.configstrings[i] );
404 if( length > MAX_QPATH ) {
405 length = MAX_QPATH;
406 }
407 MSG_WriteData( sv.configstrings[i], length );
408 MSG_WriteByte( 0 );
409 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
410 }
411 }
412
413 // send layout
414 if( client->scoreboard == SBOARD_FOLLOW ) {
415 MSG_WriteByte( svc_layout );
416 MSG_WriteString( player->layout );
417 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
418
419 client->ps.stats[STAT_LAYOUTS] = ps->ps.stats[STAT_LAYOUTS] & 1;
420 }
421
422 }
423
MVD_GameFollowCycle(mvdClient_t * client,int dir)424 static void MVD_GameFollowCycle( mvdClient_t *client, int dir ) {
425 mvdFrame_t *frame;
426 playerStateEx_t *ps;
427 int i, j;
428
429 if( client->admin ) {
430 frame = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
431 } else {
432 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
433 }
434
435 if( !frame->numPlayerStates ) {
436 return;
437 }
438
439 for( i = 0; i < frame->numPlayerStates; i++ ) {
440 j = ( frame->firstPlayerState + i ) % mvd.maxPlayerStates;
441 ps = &mvd.playerStates[j];
442 if( ps->number == client->followClientNum ) {
443 i += dir;
444 break;
445 }
446 }
447
448 i %= frame->numPlayerStates;
449 j = ( frame->firstPlayerState + i ) % mvd.maxPlayerStates;
450 ps = &mvd.playerStates[j];
451
452 MVD_GameStartFollowing( client, ps->number );
453 }
454
MVD_SetActiveState(void)455 static void MVD_SetActiveState( void ) {
456 client_t *client;
457
458 mvd.state = MVD_ACTIVE;
459
460 FOR_EACH_CLIENT( client ) {
461 if( client->state != cs_ready ) {
462 continue;
463 }
464 Com_DPrintf( "Going from cs_ready to cs_spawned for %s\n",
465 client->name );
466 client->state = cs_spawned;
467 client->sendTime = 0;
468 client->surpressCount = 0;
469 client->commandMsec = 1800;
470
471 // call the game begin function
472 sv_client = client;
473 sv_player = client->edict;
474 ge->ClientBegin( client->edict );
475 sv_client = NULL;
476 sv_player = NULL;
477
478 }
479 }
480
481 typedef struct {
482 byte *data;
483 int mask;
484 int offset;
485 int last;
486 } mvdReadStruct_t;
487
MVD_ReadByte(mvdReadStruct_t * msg)488 int MVD_ReadByte( mvdReadStruct_t *msg ) {
489 int c, mask;
490 byte *data;
491
492 if( msg->offset + 1 > msg->last ) {
493 c = -1;
494 } else {
495 data = msg->data;
496 mask = msg->mask;
497 c = ( unsigned char )data[msg->offset & mask];
498 }
499 msg->offset++;
500
501 return c;
502 }
503
MVD_ReadShort(mvdReadStruct_t * msg)504 int MVD_ReadShort( mvdReadStruct_t *msg ) {
505 int c, mask, ofs;
506 byte *data;
507
508 if( msg->offset + 2 > msg->last ) {
509 c = -1;
510 } else {
511 mask = msg->mask;
512 data = msg->data;
513 ofs = msg->offset;
514 c = ( short )( data[ofs & mask]
515 + ( data[( ofs + 1 ) & mask] << 8 ) );
516 }
517
518 msg->offset += 2;
519
520 return c;
521 }
522
MVD_ReadLong(mvdReadStruct_t * msg)523 int MVD_ReadLong( mvdReadStruct_t *msg ) {
524 int c, mask, ofs;
525 byte *data;
526
527 if( msg->offset + 4 > msg->last ) {
528 c = -1;
529 } else {
530 mask = msg->mask;
531 data = msg->data;
532 ofs = msg->offset;
533 c = data[ofs & mask]
534 + ( data[( ofs + 1 ) & mask] << 8 )
535 + ( data[( ofs + 2 ) & mask] << 16 )
536 + ( data[( ofs + 3 ) & mask] << 24 );
537 }
538
539 msg->offset += 4;
540
541 return c;
542 }
543
MVD_ReadString(mvdReadStruct_t * msg)544 char *MVD_ReadString( mvdReadStruct_t *msg ) {
545 static char string[MAX_NET_STRING];
546 int l,c;
547
548 l = 0;
549 do {
550 c = MVD_ReadByte( msg );
551 if( c == -1 || c == 0 ) {
552 break;
553 }
554 if( c == 0xFF ) {
555 c = '.';
556 }
557 string[l] = c;
558 l++;
559 } while( l < sizeof( string ) - 1 );
560
561 string[l] = 0;
562
563 return string;
564 }
565
MVD_Multicast(qboolean unbuffered,vec3_t origin,multicast_t to)566 void MVD_Multicast( qboolean unbuffered, vec3_t origin, multicast_t to ) {
567 client_t *client;
568 byte *mask;
569 cleaf_t *leaf;
570 int cluster;
571 int area1, area2;
572 int flags;
573 vec3_t org;
574 mvdClient_t *mvdcl;
575 player_state_t *ps;
576 mvdGamestate_t *gs;
577
578 if( unbuffered ) {
579 gs = &mvd.gamestates[mvd.gamestateSequence & GAMESTATE_MASK];
580 } else {
581 gs = &mvd.gamestates[mvd.activeGamestateSequence & GAMESTATE_MASK];
582 }
583
584 flags = 0;
585
586 switch( to ) {
587 case MULTICAST_ALL_R:
588 flags |= MSG_RELIABLE; // intentional fallthrough
589 case MULTICAST_ALL:
590 area1 = 0;
591 cluster = 0;
592 mask = NULL;
593 break;
594
595 case MULTICAST_PHS_R:
596 flags |= MSG_RELIABLE; // intentional fallthrough
597 case MULTICAST_PHS:
598 leaf = CM_PointLeaf( &gs->cm, origin );
599 area1 = CM_LeafArea( leaf );
600 cluster = CM_LeafCluster( leaf );
601 mask = CM_ClusterPHS( &gs->cm, cluster );
602 break;
603
604 case MULTICAST_PVS_R:
605 flags |= MSG_RELIABLE; // intentional fallthrough
606 case MULTICAST_PVS:
607 leaf = CM_PointLeaf( &gs->cm, origin );
608 area1 = CM_LeafArea( leaf );
609 cluster = CM_LeafCluster( leaf );
610 mask = CM_ClusterPVS( &gs->cm, cluster );
611 break;
612
613 default:
614 mask = NULL;
615 area1 = 0;
616 Com_Error( ERR_DROP, "MVD_Multicast: bad to: %i", to );
617 }
618
619 // send the data to all relevent clients
620 FOR_EACH_CLIENT( client ) {
621 if( client->state < cs_connected ) {
622 continue;
623 }
624 if( client->protocol == PROTOCOL_VERSION_MVD ) {
625 continue;
626 }
627 mvdcl = ( mvdClient_t * )client->edict->client;
628 if( mvdcl->admin != unbuffered ) {
629 continue;
630 }
631
632 if( !( flags & MSG_RELIABLE ) ) {
633 /* do not send unreliables to connecting clients */
634 if( client->state != cs_spawned || client->download || client->nodata ) {
635 continue;
636 }
637 }
638
639 if( mask ) {
640 // find the client's PVS
641 ps = &client->edict->client->ps;
642 VectorMA( ps->viewoffset, 0.125f, ps->pmove.origin, org );
643 leaf = CM_PointLeaf( &gs->cm, org );
644 area2 = CM_LeafArea( leaf );
645 if( !CM_AreasConnected( &gs->cm, area1, area2 ) )
646 continue;
647 cluster = CM_LeafCluster( leaf );
648 if( !Q_IsBitSet( mask, cluster ) ) {
649 continue;
650 }
651 }
652
653 SV_ClientAddMessage( client, flags );
654 }
655
656 /* clear the buffer */
657 SZ_Clear( &msg_write );
658
659 }
660
MVD_FrameParseMulticast(mvdReadStruct_t * msg)661 static void MVD_FrameParseMulticast( mvdReadStruct_t *msg ) {
662 multicast_t to;
663 int length;
664 union {
665 vec_t v[3];
666 uint32 l[3];
667 } origin;
668 int i, c;
669
670 length = MVD_ReadShort( msg );
671 to = ( length >> 12 ) & 7;
672 length &= 0xFFF;
673
674 if( to > MULTICAST_PVS_R ) {
675 Com_Error( ERR_DROP, "MVD_FrameParseMulticast: bad to" );
676 }
677
678 if( length < 1 ) {
679 Com_Error( ERR_DROP, "MVD_FrameParseMulticast: bad length" );
680 }
681
682 if( to != MULTICAST_ALL && to != MULTICAST_ALL_R ) {
683 origin.l[0] = MVD_ReadLong( msg );
684 origin.l[1] = MVD_ReadLong( msg );
685 origin.l[2] = MVD_ReadLong( msg );
686 } else {
687 VectorClear( origin.v );
688 }
689
690 if( msg->offset + length > msg->last ) {
691 Com_Error( ERR_DROP,
692 "MVD_FrameParseMulticast: read past end of message" );
693 }
694
695 /* copy it byte-to-byte */
696 for( i = 0; i < length; i++ ) {
697 c = MVD_ReadByte( msg );
698 MSG_WriteByte( c );
699 }
700
701 MVD_Multicast( qfalse, origin.v, to );
702
703 }
704
MVD_FrameParseUnicast(mvdReadStruct_t * msg)705 static void MVD_FrameParseUnicast( mvdReadStruct_t *msg ) {
706 int clientNum, length, last;
707 int flags;
708 client_t *client;
709 mvdClient_t *gclient;
710 mvdPlayer_t *player;
711 int i, c, offset;
712 char *s;
713 qboolean gotLayout, wantLayout;
714
715 flags = 0;
716 clientNum = MVD_ReadByte( msg );
717 if( clientNum & 0x80 ) {
718 flags |= MSG_RELIABLE;
719 }
720 clientNum &= 0x7F;
721 length = MVD_ReadShort( msg );
722
723 if( mvd_debug->integer > 1 ) {
724 Com_Printf( "Unicast to %d (%d bytes)\n", clientNum, length );
725 }
726
727 if( ( unsigned )clientNum >= mvd.maxPlayers ) {
728 Com_Error( ERR_DROP, "MVD_FrameParseUnicast: bad clientNum" );
729 }
730
731 if( length < 1 ) {
732 Com_Error( ERR_DROP, "MVD_FrameParseUnicast: bad length" );
733 }
734
735 last = msg->offset + length;
736 if( last > msg->last ) {
737 Com_Error( ERR_DROP, "MVD_FrameParseUnicast: read past end of message" );
738 }
739
740 player = &mvd.players[clientNum];
741
742 gotLayout = qfalse;
743
744
745 /* attempt to parse the datagram and find custom configstrings,
746 * layouts, etc. give up if unknown command byte is encountered */
747 while( 1 ) {
748 if( msg->offset > last ) {
749 Com_Error( ERR_DROP,
750 "MVD_FrameParseUnicast: read past end of message" );
751 }
752
753 if( msg->offset == last ) {
754 break;
755 }
756
757 if( ( c = MVD_ReadByte( msg ) ) == -1 ) {
758 break;
759 }
760
761 if( mvd_debug->integer > 1 ) {
762 Com_Printf( "%s\n", MSG_ServerCommandString( c ) );
763 }
764
765 switch( c ) {
766 case svc_layout:
767 s = MVD_ReadString( msg );
768 Q_strncpyz( player->layout, s, sizeof( player->layout ) );
769 gotLayout = qtrue;
770 break;
771 case svc_configstring:
772 i = MVD_ReadShort( msg );
773 s = MVD_ReadString( msg );
774 if( ( unsigned )i >= MAX_CONFIGSTRINGS ) {
775 Com_Error( ERR_DROP,
776 "MVD_FrameParseUnicast: bad configstring index" );
777 }
778 if( player->configstrings[i] ) {
779 Z_Free( player->configstrings[i] );
780 }
781 player->configstrings[i] = MVD_CopyString( s );
782 MSG_WriteByte( svc_configstring );
783 MSG_WriteShort( i );
784 MSG_WriteString( s );
785 break;
786 case svc_print:
787 i = MVD_ReadByte( msg );
788 s = MVD_ReadString( msg );
789 MSG_WriteByte( svc_print );
790 MSG_WriteByte( i );
791 MSG_WriteString( s );
792 break;
793 case svc_stufftext:
794 s = MVD_ReadString( msg );
795 MSG_WriteByte( svc_stufftext );
796 MSG_WriteString( s );
797 break;
798 default:
799 /* copy the rest byte-to-byte */
800 MSG_WriteByte( c );
801 for( offset = msg->offset; offset < last; offset++ ) {
802 c = MVD_ReadByte( msg );
803 MSG_WriteByte( c );
804 }
805 msg->offset = offset;
806 goto breakOut;
807 }
808 }
809
810 breakOut:
811 /* send to all relevant clients */
812 wantLayout = qfalse;
813 FOR_EACH_CLIENT( client ) {
814 if( client->state < cs_spawned ) {
815 continue;
816 }
817 if( client->protocol == PROTOCOL_VERSION_MVD ) {
818 continue;
819 }
820 gclient = ( mvdClient_t * )client->edict->client;
821 if( gclient->admin ) {
822 continue;
823 }
824 if( gclient->scoreboard == SBOARD_SCORES ) {
825 if( mvd.serverProtocol != PROTOCOL_VERSION_MVD ||
826 clientNum == mvd.clientNum )
827 {
828 wantLayout = qtrue;
829 }
830 }
831 if( !gclient->following ) {
832 if( mvd.serverProtocol == PROTOCOL_VERSION_MVD &&
833 clientNum == mvd.clientNum )
834 {
835 SV_ClientAddMessage( client, flags );
836 }
837 continue;
838 }
839 if( gclient->followClientNum == clientNum ) {
840 SV_ClientAddMessage( client, flags );
841 if( gclient->scoreboard == SBOARD_FOLLOW ) {
842 wantLayout = qtrue;
843 }
844 }
845 }
846
847 SZ_Clear( &msg_write );
848
849 if( !gotLayout || !wantLayout ) {
850 return;
851 }
852
853 MSG_WriteByte( svc_layout );
854 MSG_WriteString( player->layout );
855
856 FOR_EACH_CLIENT( client ) {
857 if( client->state < cs_spawned ) {
858 continue;
859 }
860 if( client->protocol == PROTOCOL_VERSION_MVD ) {
861 continue;
862 }
863 gclient = ( mvdClient_t * )client->edict->client;
864 if( gclient->admin ) {
865 continue;
866 }
867 if( gclient->scoreboard == SBOARD_SCORES ) {
868 if( mvd.serverProtocol != PROTOCOL_VERSION_MVD ||
869 clientNum == mvd.clientNum )
870 {
871 SV_ClientAddMessage( client, flags );
872 continue;
873 }
874 }
875 if( gclient->followClientNum == clientNum ) {
876 if( gclient->scoreboard == SBOARD_FOLLOW ) {
877 SV_ClientAddMessage( client, flags );
878 }
879 }
880 }
881
882 SZ_Clear( &msg_write );
883 }
884
MVD_FrameParseConfigstring(mvdReadStruct_t * msg)885 static void MVD_FrameParseConfigstring( mvdReadStruct_t *msg ) {
886 int index, length;
887 char *string;
888 client_t *client;
889 mvdClient_t *mvdcl;
890
891 index = MVD_ReadShort( msg );
892 string = MVD_ReadString( msg );
893
894 if( ( unsigned )index >= MAX_CONFIGSTRINGS ) {
895 Com_Error( ERR_DROP,
896 "MVD_FrameParseConfigstring: bad index: %d\n", index );
897 }
898
899 length = strlen( string );
900
901 if( sizeof( sv.configstrings[0] ) * index + length >
902 sizeof( sv.configstrings ) - 1 )
903 {
904 Com_Error( ERR_DROP,
905 "MVD_FrameParseConfigstring: oversize configstring: %d", index );
906 }
907
908 if( !strcmp( sv.configstrings[index], string ) ) {
909 return;
910 }
911
912 strcpy( sv.configstrings[index], string );
913
914 MSG_WriteByte( svc_configstring );
915 MSG_WriteShort( index );
916 MSG_WriteString( string );
917
918 FOR_EACH_CLIENT( client ) {
919 if( client->state < cs_connected ) {
920 continue;
921 }
922 if( client->protocol == PROTOCOL_VERSION_MVD ) {
923 continue;
924 }
925 mvdcl = ( mvdClient_t * )client->edict->client;
926 if( mvdcl->admin ) {
927 continue;
928 }
929 SV_ClientAddMessage( client, MSG_RELIABLE );
930 }
931
932 SZ_Clear( &msg_write );
933 }
934
MVD_FrameParseMessages(mvdReadStruct_t * msg)935 static void MVD_FrameParseMessages( mvdReadStruct_t *msg ) {
936 int cmd;
937
938 //
939 // parse the message
940 //
941 while( 1 ) {
942 if( msg->offset > msg->last ) {
943 Com_Error( ERR_DROP,
944 "MVD_FrameParseMessages: read past end of message" );
945 }
946
947 if( ( cmd = MVD_ReadByte( msg ) ) == -1 ) {
948 break;
949 }
950
951 // MVD_ShowSVC(cmd);
952
953 switch( cmd ) {
954 case svc_multicast:
955 MVD_FrameParseMulticast( msg );
956 break;
957 case svc_unicast:
958 MVD_FrameParseUnicast( msg );
959 break;
960 case svc_configstring:
961 MVD_FrameParseConfigstring( msg );
962 break;
963 default:
964 Com_Error( ERR_DROP,
965 "MVD_FrameParseMessages: illegible command: %d", cmd );
966 break;
967 }
968 }
969 }
970
MVD_TransitionGamestate(int sequence)971 void MVD_TransitionGamestate( int sequence ) {
972 mvdConfigstring_t *cs, *nextcs;
973 mvdGamestate_t *gs;
974 mvdPlayer_t *player, *last;
975 client_t *client;
976 mvdClient_t *mvdcl;
977 int i, j;
978
979 Com_Printf( "------- MVD_TransitionGamestate -------\n" );
980
981 MVD_DPrintf( "Going from %d to %d (frame %d)\n",
982 mvd.activeGamestateSequence, sequence, mvd.activePacketNum );
983
984 Cvar_Set( "timedemo", "0" );
985 Cvar_Set( "sv_paused", "0" );
986
987 if( sequence < mvd.activeGamestateSequence ) {
988 Com_Error( ERR_DROP, "MVD_TransitionGamestate: sequence < active" );
989 }
990
991 /* free old gamestate */
992 for( j = mvd.activeGamestateSequence; j < sequence; j++ ) {
993 gs = &mvd.gamestates[j & GAMESTATE_MASK];
994
995 for( i = 0; i < SV_BASELINES_CHUNKS; i++ ) {
996 if( gs->baselines[i] ) {
997 Z_Free( gs->baselines[i] );
998 }
999 }
1000 for( cs = gs->headCS; cs; cs = nextcs ) {
1001 nextcs = cs->next;
1002 Z_Free( cs );
1003 }
1004 CM_FreeMap( &gs->cm );
1005 memset( gs, 0, sizeof( *gs ) );
1006 }
1007
1008 gs = &mvd.gamestates[sequence & GAMESTATE_MASK];
1009 mvd.activeGamestateSequence = sequence;
1010
1011 /* reset player slots */
1012 last = mvd.players + mvd.maxPlayers;
1013 for( player = mvd.players; player != last; player++ ) {
1014 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
1015 if( player->configstrings[i] ) {
1016 Z_Free( player->configstrings[i] );
1017 }
1018 }
1019 memset( player, 0, sizeof( *player ) );
1020 }
1021
1022 MSG_WriteByte( svc_stufftext );
1023 MSG_WriteString( "changing\n" );
1024
1025 FOR_EACH_CLIENT( client ) {
1026 if( client->state < cs_connected ) {
1027 continue;
1028 }
1029 if( client->protocol == PROTOCOL_VERSION_MVD ) {
1030 continue;
1031 }
1032 mvdcl = ( mvdClient_t * )client->edict->client;
1033 if( mvdcl->admin ) {
1034 continue;
1035 }
1036
1037 memset( &mvdcl->lastcmd, 0, sizeof( mvdcl->lastcmd ) );
1038
1039 client->state = cs_connected;
1040 client->lastframe = -1;
1041 client->sendTime = 0;
1042 client->surpressCount = 0;
1043
1044 SV_ClientAddMessage( client, MSG_RELIABLE );
1045 }
1046
1047 SZ_Clear( &msg_write );
1048
1049 SV_SendAsyncPackets();
1050
1051 memset( sv.configstrings, 0, sizeof( sv.configstrings ) );
1052
1053 i = strlen( gs->mapname );
1054 if( i > 9 ) {
1055 Q_strncpyz( sv.name, gs->mapname + 5, sizeof( sv.name ) ); // skip "maps/"
1056 sv.name[i - 9] = 0; // cut off ".bsp"
1057 } else {
1058 Q_strncpyz( sv.name, gs->mapname, sizeof( sv.name ) );
1059 }
1060
1061 Com_Printf( "Map : %s\n", sv.name );
1062 Com_Printf( "Max players : %d\n", gs->maxclients );
1063
1064 svs.spawncount = ( rand() | ( rand() << 16 ) ) ^ Sys_Realtime();
1065 svs.spawncount &= 0x7FFFFFFF; // any partially connected client will be
1066 // restarted
1067
1068 /* set up configstrings */
1069 for( cs = gs->headCS; cs; cs = cs->next ) {
1070 strcpy( sv.configstrings[cs->index], cs->string );
1071 }
1072
1073 MSG_WriteByte( svc_stufftext );
1074 MSG_WriteString( "reconnect\n" );
1075
1076 FOR_EACH_CLIENT( client ) {
1077 if( client->state < cs_connected ) {
1078 continue;
1079 }
1080 if( client->protocol == PROTOCOL_VERSION_MVD ) {
1081 continue;
1082 }
1083 mvdcl = ( mvdClient_t * )client->edict->client;
1084 if( mvdcl->admin ) {
1085 continue;
1086 }
1087
1088 SV_ClientAddMessage( client, MSG_RELIABLE );
1089 }
1090
1091 SZ_Clear( &msg_write );
1092
1093 /* set serverinfo variable */
1094 Cvar_FullSet( "mapname", sv.name, CVAR_SERVERINFO|CVAR_NOSET, CVAR_SET_DIRECT );
1095
1096 Cvar_SetInteger( "sv_running", ss_broadcast );
1097 Cvar_SetInteger( "sv_paused", 0 );
1098
1099 sv.state = ss_broadcast;
1100
1101 Com_Printf( "---------------------------------------\n" );
1102 }
1103
1104 #define RESET_DELTA 16
1105
MVD_DriftTime(void)1106 static void MVD_DriftTime( void ) {
1107 int delta, drift;
1108
1109 delta = mvd.serverPacketNum - mvd.activePacketNum + 1;
1110 drift = mvd_buffer_size->integer - delta;
1111 clamp( drift, -RESET_DELTA, RESET_DELTA );
1112
1113 if( mvd_debug->integer > 2 ) {
1114 Com_Printf( "MVD_DriftTime: frame=%d delta=%d ",
1115 mvd.activePacketNum, delta );
1116 if( drift > 0 ) {
1117 Com_Printf( "[fast]\n" );
1118 } else if( drift < 0 ) {
1119 Com_Printf( "[slow]\n" );
1120 } else {
1121 Com_Printf( "\n" );
1122 }
1123 }
1124
1125 sv.time += drift;
1126 }
1127
MVD_TransitionFrame(mvdFrame_t * frame)1128 static void MVD_TransitionFrame( mvdFrame_t *frame ) {
1129 mvdReadStruct_t msg;
1130 mvdGamestate_t *gs;
1131
1132 /* add unreliable datagram messages present in this frame */
1133 if( frame->numMessageBytes ) {
1134 msg.data = mvd.messageBytes;
1135 msg.mask = mvd.maxMessageBytes - 1;
1136 msg.offset = frame->firstMessageByte;
1137 msg.last = frame->firstMessageByte + frame->numMessageBytes;
1138 if( mvd_debug->integer > 1 ) {
1139 Com_Printf( "Adding %d bytes of unreliable messages\n",
1140 msg.last - msg.offset );
1141 }
1142 MVD_FrameParseMessages( &msg );
1143 }
1144
1145 /* update areaportals */
1146 if( mvd.serverProtocol == PROTOCOL_VERSION_MVD ) {
1147 gs = &mvd.gamestates[mvd.activeGamestateSequence & GAMESTATE_MASK];
1148 CM_SetPortalStates( &gs->cm, frame->portalbytes, frame->numPortalBytes );
1149 }
1150 }
1151
1152 /*
1153 MVD delay buffer:
1154 mvd_buffer_size
1155 | |<------------------------------------------------->|
1156 |__________|__________|________________________________________|
1157 | 16 | 16 | |
1158 |<-------->|<-------->| |
1159 A B C D
1160
1161 If mvd.state == MVD_BUFFERING, do nothing until we are beyond point B.
1162 If mvd.state == MVD_ACTIVE and we are between A and C, try to reach
1163 point B by slowly drifting sv.time.
1164 If we are ahead point C, restart buffering (with a 16 frames hysteresis).
1165 If we are beyond point A, or we have run out of entityState or playerState
1166 buffers, explicitly set mvd.activePacketNum at point B.
1167
1168 When playing back a demo, just run towards point D and do nothing else.
1169 */
MVD_SetNextFrame(void)1170 static void MVD_SetNextFrame( void ) {
1171 mvdFrame_t *frame;
1172 mvdReadStruct_t msg;
1173 int delta;
1174
1175 if( mvd.state < MVD_BUFFERING ) {
1176 return;
1177 }
1178
1179 delta = mvd.serverPacketNum - mvd.activePacketNum;
1180 if( !mvd.demoplayback ) {
1181 if( delta < mvd_buffer_size->integer - RESET_DELTA &&
1182 mvd.state == MVD_ACTIVE )
1183 {
1184 /* start buffering again */
1185 mvd.state = MVD_BUFFERING;
1186 MVD_DPrintf( "Restarted buffering at frame %d, delta is %d\n",
1187 mvd.serverPacketNum, delta );
1188 return;
1189 }
1190 if( delta < mvd_buffer_size->integer && mvd.state == MVD_BUFFERING ) {
1191 MVD_DPrintf( "Still buffering at frame %d, delta is %d\n",
1192 mvd.serverPacketNum, delta );
1193 return;
1194 }
1195 }
1196
1197 if( delta > mvd.frameBackup - 1 ) {
1198 MVD_DPrintf( "MVD_SetNextFrame: frame %d is too old, delta is %d\n",
1199 mvd.activePacketNum, delta );
1200 mvd.activePacketNum = mvd.serverPacketNum - mvd_buffer_size->integer;
1201 }
1202
1203 frame = NULL;
1204 while( mvd.activePacketNum < mvd.serverPacketNum ) {
1205 mvd.activePacketNum++;
1206 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
1207
1208 if( frame->serverPacketNum != mvd.activePacketNum ) {
1209 MVD_DPrintf( "MVD_SetNextFrame: %d: never received\n",
1210 mvd.activePacketNum );
1211 continue;
1212 }
1213
1214 /* got new gamestate? */
1215 if( frame->gamestateSequence != mvd.activeGamestateSequence ) {
1216 MVD_TransitionGamestate( frame->gamestateSequence );
1217 }
1218
1219 /* does this frame contain svc_frame? */
1220 if( !( frame->flags & MFF_ACTIVE ) ) {
1221 continue;
1222 }
1223
1224 if( !( frame->flags & MFF_VALID ) ) {
1225 MVD_DPrintf( "MVD_SetNextFrame: %d: invalid frame\n",
1226 mvd.activePacketNum );
1227 continue;
1228 }
1229
1230 if( mvd.nextEntityStates - frame->firstEntityState >
1231 mvd.maxEntityStates )
1232 {
1233 MVD_DPrintf( "MVD_SetNextFrame: %d: entityStates too old\n",
1234 mvd.activePacketNum );
1235 continue;
1236 }
1237 if( mvd.nextPlayerStates - frame->firstPlayerState >
1238 mvd.maxPlayerStates )
1239 {
1240 MVD_DPrintf( "MVD_SetNextFrame: %d: playerStates too old\n",
1241 mvd.activePacketNum );
1242 continue;
1243 }
1244
1245 if( mvd.nextMessageBytes - frame->firstMessageByte >
1246 mvd.maxMessageBytes )
1247 {
1248 /* FIXME: use this frame anyway? */
1249 MVD_DPrintf( "MVD_SetNextFrame: %d: messages too old\n",
1250 mvd.activePacketNum );
1251 continue;
1252 }
1253
1254 if( !mvd.demoplayback ) {
1255 MVD_DriftTime();
1256 }
1257
1258 /* advance server state to this frame */
1259 mvd.prevPacketNum = mvd.activePacketNum - 1;
1260 MVD_TransitionFrame( frame );
1261
1262 /* getting a valid frame message ends the buffering process */
1263 if( mvd.state == MVD_BUFFERING ) {
1264 delta = mvd.serverPacketNum - mvd.activePacketNum;
1265 MVD_DPrintf( "Finished buffering at frame %d, delta is %d\n",
1266 mvd.serverPacketNum, delta );
1267 MVD_SetActiveState();
1268 }
1269
1270 break;
1271
1272 }
1273
1274 if( !frame ) {
1275 return;
1276 }
1277
1278 /* add pending reliable datagram messages */
1279 msg.data = mvd.reliableMessageBytes;
1280 msg.mask = mvd.maxReliableMessageBytes - 1;
1281 msg.offset = mvd.lastReliableMessageBytes;
1282 msg.last = frame->firstReliableMessageByte + frame->numReliableMessageBytes;
1283 if( msg.last == msg.offset ) {
1284 return;
1285 }
1286
1287 if( msg.last < msg.offset ) {
1288 if( mvd.demoplayback ) {
1289 /* this is normal if demo was just played backwards */
1290 return;
1291 }
1292 Com_Error( ERR_DROP,
1293 "MVD_SetNextFrame: reliable messages went backwards" );
1294 }
1295
1296 if( mvd.nextReliableMessageBytes - mvd.lastReliableMessageBytes >
1297 mvd.maxReliableMessageBytes )
1298 {
1299 Com_Error( ERR_DROP, "MVD_SetNextFrame: dropped reliable messages" );
1300 }
1301
1302 if( mvd_debug->integer > 1 ) {
1303 Com_Printf( "Adding %d bytes of reliable messages: %d --> %d\n",
1304 msg.last - msg.offset, msg.offset, msg.last );
1305 }
1306 MVD_FrameParseMessages( &msg );
1307 mvd.lastReliableMessageBytes = msg.last;
1308 }
1309
1310 /* used for playing demo buffer backwards */
MVD_SetPrevFrame(void)1311 static void MVD_SetPrevFrame( void ) {
1312 mvdFrame_t *frame;
1313
1314 while( mvd.activePacketNum > 0 &&
1315 mvd.serverPacketNum - mvd.activePacketNum < mvd.frameBackup - 1 )
1316 {
1317 mvd.activePacketNum--;
1318 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
1319
1320 if( frame->serverPacketNum != mvd.activePacketNum ) {
1321 MVD_DPrintf( "MVD_SetPrevFrame: %d: never received\n",
1322 mvd.activePacketNum );
1323 continue;
1324 }
1325
1326 /* got new gamestate? */
1327 if( frame->gamestateSequence != mvd.activeGamestateSequence ) {
1328 MVD_DPrintf( "MVD_SetPrevFrame: different gamestateSequence\n" );
1329 mvd.activePacketNum++;
1330 break;
1331 }
1332
1333 /* does this frame contain svc_frame? */
1334 if( !( frame->flags & MFF_ACTIVE ) ) {
1335 continue;
1336 }
1337
1338 if( !( frame->flags & MFF_VALID ) ) {
1339 MVD_DPrintf( "MVD_SetPrevFrame: %d: invalid frame\n",
1340 mvd.activePacketNum );
1341 continue;
1342 }
1343
1344 if( mvd.nextEntityStates - frame->firstEntityState >
1345 mvd.maxEntityStates )
1346 {
1347 MVD_DPrintf( "MVD_SetPrevFrame: %d: entityStates too old\n",
1348 mvd.activePacketNum );
1349 mvd.activePacketNum++;
1350 break;
1351 }
1352 if( mvd.nextPlayerStates - frame->firstPlayerState >
1353 mvd.maxPlayerStates )
1354 {
1355 MVD_DPrintf( "MVD_SetPrevFrame: %d: playerStates too old\n",
1356 mvd.activePacketNum );
1357 mvd.activePacketNum++;
1358 break;
1359 }
1360
1361 if( mvd.nextMessageBytes - frame->firstMessageByte >
1362 mvd.maxMessageBytes )
1363 {
1364 MVD_DPrintf( "MVD_SetPrevFrame: %d: messages too old\n",
1365 mvd.activePacketNum );
1366 mvd.activePacketNum++;
1367 break;
1368 }
1369
1370 /* advance server state to this frame */
1371 mvd.prevPacketNum = mvd.activePacketNum + 1;
1372 MVD_TransitionFrame( frame );
1373 break;
1374 }
1375
1376 }
1377
MVD_BackwardsDown_f(void)1378 void MVD_BackwardsDown_f( void ) {
1379 mvd.demobackwards = qtrue;
1380 }
1381
MVD_BackwardsUp_f(void)1382 void MVD_BackwardsUp_f( void ) {
1383 mvd.demobackwards = qfalse;
1384 }
1385
MVD_GameInit(void)1386 static void MVD_GameInit( void ) {
1387 int i;
1388
1389 Com_Printf( "----- MVD_GameInit -----\n" );
1390
1391 mvd.clients = Z_TagMallocz( sizeof( mvdClient_t ) * sv_maxclients->integer,
1392 TAG_GAME );
1393
1394 mvd.players = MVD_Mallocz( sizeof( mvdPlayer_t ) * mvd.maxPlayers );
1395
1396 /* allocate delay buffers */
1397 Cvar_ClampInteger( mvd_buffer_size, RESET_DELTA, 1024 );
1398
1399 mvd.frameBackup = mvd_buffer_size->integer + RESET_DELTA;
1400 Com_DPrintf( "Delay buffer of %d frames total\n", mvd.frameBackup );
1401
1402 mvd.frames = MVD_Mallocz( sizeof( mvdFrame_t ) * mvd.frameBackup );
1403 mvd.maxEntityStates = MAX_EDICTS * mvd.frameBackup;
1404 mvd.maxPlayerStates = mvd.maxPlayers * mvd.frameBackup;
1405
1406 mvd.entityStates = MVD_Mallocz( sizeof( entityStateEx_t ) *
1407 mvd.maxEntityStates );
1408 mvd.playerStates = MVD_Mallocz( sizeof( playerStateEx_t ) *
1409 mvd.maxPlayerStates );
1410
1411 i = 256 * mvd.frameBackup; /* TODO */
1412 mvd.maxMessageBytes = Q_CeilPowerOfTwo( i );
1413 mvd.nextMessageBytes = 0;
1414 mvd.messageBytes = MVD_Mallocz( mvd.maxMessageBytes );
1415
1416 i = 256 * mvd.maxPlayers * mvd.frameBackup; /* TODO */
1417 mvd.maxReliableMessageBytes = Q_CeilPowerOfTwo( i );
1418 mvd.nextReliableMessageBytes = 0;
1419 mvd.reliableMessageBytes = MVD_Mallocz( mvd.maxReliableMessageBytes );
1420
1421 /* prepare client slots */
1422 for( i = 0; i < sv_maxclients->integer; i++ ) {
1423 mvd_edicts[i + 1].client = ( gclient_t * )&mvd.clients[i];
1424 mvd.clients[i].cl = &svs.clientpool[i];
1425 }
1426 }
1427
MVD_GameShutdown(void)1428 static void MVD_GameShutdown( void ) {
1429 Com_Printf( "----- MVD_GameShutdown -----\n" );
1430
1431 mvd_ge.num_edicts = 0;
1432 MVD_Disconnect();
1433
1434 }
1435
MVD_GameSpawnEntities(char * mapname,char * entstring,char * spawnpoint)1436 void MVD_GameSpawnEntities( char *mapname, char *entstring, char *spawnpoint ) {
1437 }
MVD_GameWriteGame(char * filename,qboolean autosave)1438 static void MVD_GameWriteGame( char *filename, qboolean autosave ) {
1439 }
MVD_GameReadGame(char * filename)1440 static void MVD_GameReadGame( char *filename ) {
1441 }
MVD_GameWriteLevel(char * filename)1442 static void MVD_GameWriteLevel( char *filename ) {
1443 }
MVD_GameReadLevel(char * filename)1444 static void MVD_GameReadLevel( char *filename ) {
1445 }
1446
MVD_GameClientConnect(edict_t * ent,char * userinfo)1447 static qboolean MVD_GameClientConnect( edict_t *ent, char *userinfo ) {
1448 mvdClient_t *client;
1449
1450 client = ( mvdClient_t * )ent->client;
1451
1452 if( client->cl->protocol != PROTOCOL_VERSION_MVD ) {
1453 SV_BroadcastPrintf( PRINT_HIGH, "[MVD] %s connected\n",
1454 Info_ValueForKey( userinfo, "name" ) );
1455 }
1456 return qtrue;
1457 }
1458
MVD_GameClientBegin(edict_t * ent)1459 static void MVD_GameClientBegin( edict_t *ent ) {
1460 mvdClient_t *client;
1461 char *s;
1462
1463 client = ( mvdClient_t * )ent->client;
1464 client->savedClientNum = CLIENTNUM_NONE;
1465
1466 client->floodTime = 0;
1467 client->floodHead = 0;
1468
1469 if( client->cl->protocol == PROTOCOL_VERSION_MVD ) {
1470 client->connected = qtrue;
1471 return;
1472 }
1473
1474 if( !client->connected ) {
1475 SV_BroadcastPrintf( PRINT_HIGH, "[MVD] %s entered the server\n",
1476 client->cl->name );
1477 client->connected = qtrue;
1478 }
1479
1480 if( mvd_motd->string[0] ) {
1481 s = Cmd_MacroExpandString( mvd_motd->string, qfalse );
1482 if( !s ) {
1483 Com_WPrintf( "Macro expansion of mvd_motd failed\n" );
1484 Cvar_Set( "mvd_motd", "" );
1485 } else {
1486 s = Q_UnescapeString( s );
1487 MSG_WriteByte( svc_centerprint );
1488 MSG_WriteString( s );
1489 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
1490 }
1491 }
1492
1493 MVD_GameSetValidPos( client );
1494 if( mvd.serverProtocol != PROTOCOL_VERSION_MVD ) {
1495 MVD_GameStartFollowing( client, CLIENTNUM_NONE );
1496 }
1497 MVD_SetDefaultLayout( client );
1498 }
1499
MVD_GameClientUserinfoChanged(edict_t * ent,char * userinfo)1500 static void MVD_GameClientUserinfoChanged( edict_t *ent, char *userinfo ) {
1501 mvdClient_t *client;
1502 float fov;
1503
1504 client = ( mvdClient_t * )ent->client;
1505 fov = atof( Info_ValueForKey( userinfo, "fov" ) );
1506 if( fov < 1 ) {
1507 fov = 90;
1508 } else if( fov > 160 ) {
1509 fov = 160;
1510 }
1511 client->fov = fov;
1512 if( !client->following ) {
1513 client->ps.fov = fov;
1514 }
1515 }
1516
MVD_GameClientDisconnect(edict_t * ent)1517 static void MVD_GameClientDisconnect( edict_t *ent ) {
1518 mvdClient_t *client;
1519 client_t *cl;
1520
1521 client = ( mvdClient_t * )ent->client;
1522 cl = client->cl;
1523 memset( client, 0, sizeof( *client ) );
1524 client->cl = cl;
1525
1526 if( client->cl->protocol != PROTOCOL_VERSION_MVD ) {
1527 SV_BroadcastPrintf( PRINT_HIGH, "[MVD] %s disconnected\n", cl->name );
1528 }
1529 }
1530
MVD_StartAdmin(mvdClient_t * client)1531 static void MVD_StartAdmin( mvdClient_t *client ) {
1532 int i, length;
1533 mvdFrame_t *src, *dst;
1534 playerStateEx_t *ps;
1535
1536 src = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
1537 dst = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
1538
1539 if( src->gamestateSequence != dst->gamestateSequence ) {
1540 MSG_WriteByte( svc_stufftext );
1541 MSG_WriteString( "changing; reconnect\n" );
1542 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
1543 client->cl->state = cs_connected;
1544 client->admin = qtrue;
1545 return;
1546 }
1547
1548 /* write delta configstrings */
1549 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
1550 length = strlen( mvd.configstrings[i] );
1551 if( !length && sv.configstrings[i][0] ) {
1552 MSG_WriteByte( svc_configstring );
1553 MSG_WriteShort( i );
1554 MSG_WriteByte( 0 );
1555 continue;
1556 }
1557 if( length > MAX_QPATH ) {
1558 length = MAX_QPATH;
1559 }
1560 if( strncmp( sv.configstrings[i], mvd.configstrings[i], length ) ) {
1561 MSG_WriteByte( svc_configstring );
1562 MSG_WriteShort( i );
1563 MSG_WriteData( mvd.configstrings[i], length );
1564 MSG_WriteByte( 0 );
1565 }
1566 }
1567
1568 ps = &mvd.playerStates[dst->firstPlayerState % mvd.maxPlayerStates];
1569
1570 #if 0
1571 client->delta_angles[0] = ANGLE2SHORT( ps->ps.viewangles[0] ) - ps->ps.pmove.delta_angles[0] - client->lastcmd.angles[0];
1572 client->delta_angles[1] = ANGLE2SHORT( ps->ps.viewangles[1] ) - ps->ps.pmove.delta_angles[1] - client->lastcmd.angles[1];
1573 client->delta_angles[2] = ANGLE2SHORT( ps->ps.viewangles[2] ) - ps->ps.pmove.delta_angles[2] - client->lastcmd.angles[2];
1574 #endif
1575
1576 client->admin = qtrue;
1577 client->pmflags ^= PMF_TELEPORT_BIT;
1578 if( mvd.serverProtocol < PROTOCOL_VERSION_MVD ) {
1579 // client->scoreboard = SBOARD_SCORES;
1580 // MVD_UpdateLayoutScores( client );
1581 MVD_GameStartFollowing( client, CLIENTNUM_NONE );
1582 }
1583 SV_ClientPrintf( client->cl, PRINT_HIGH, "[MVD] Granted admin status.\n" );
1584 }
1585
MVD_StopAdmin(mvdClient_t * client)1586 static void MVD_StopAdmin( mvdClient_t *client ) {
1587 int i, length;
1588 mvdFrame_t *src, *dst;
1589
1590 src = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
1591 dst = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
1592
1593 if( src->gamestateSequence != dst->gamestateSequence ) {
1594 MSG_WriteByte( svc_stufftext );
1595 MSG_WriteString( "changing; reconnect\n" );
1596 SV_ClientAddMessage( client->cl, MSG_RELIABLE|MSG_CLEAR );
1597 client->cl->state = cs_connected;
1598 client->admin = qfalse;
1599 return;
1600 }
1601
1602 /* write delta configstrings */
1603 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
1604 length = strlen( sv.configstrings[i] );
1605 if( !length && mvd.configstrings[i][0] ) {
1606 MSG_WriteByte( svc_configstring );
1607 MSG_WriteShort( i );
1608 MSG_WriteByte( 0 );
1609 continue;
1610 }
1611 if( length > MAX_QPATH ) {
1612 length = MAX_QPATH;
1613 }
1614 if( strncmp( mvd.configstrings[i], sv.configstrings[i], length ) ) {
1615 MSG_WriteByte( svc_configstring );
1616 MSG_WriteShort( i );
1617 MSG_WriteData( sv.configstrings[i], length );
1618 MSG_WriteByte( 0 );
1619 }
1620 }
1621
1622 client->admin = qfalse;
1623 client->pmflags ^= PMF_TELEPORT_BIT;
1624 SV_ClientPrintf( client->cl, PRINT_HIGH, "[MVD] Lost admin status.\n" );
1625 }
1626
MVD_GameClientCommand(edict_t * ent)1627 static void MVD_GameClientCommand( edict_t *ent ) {
1628 mvdClient_t *client;
1629 char *cmd;
1630 int clientNum, i;
1631
1632 if( mvd.state < MVD_PRIMED ) {
1633 return;
1634 }
1635
1636 client = ( mvdClient_t * )ent->client;
1637 cmd = Cmd_Argv( 0 );
1638 if( !Q_stricmp( cmd, "!mvdadmin" ) ) {
1639 if( mvd.demoplayback || client->cl->protocol == PROTOCOL_VERSION_MVD ) {
1640 return;
1641 }
1642 if( client->admin ) {
1643 MVD_StopAdmin( client );
1644 return;
1645 }
1646 if( !NET_IsLocalAddress( &client->cl->netchan->remote_address ) ) {
1647 if( Cmd_Argc() < 2 ) {
1648 SV_ClientPrintf( client->cl, PRINT_HIGH, "Usage: %s <password>\n",
1649 Cmd_Argv( 0 ) );
1650 return;
1651 }
1652 if( !mvd_admin_password->string[0]
1653 || strcmp( mvd_admin_password->string, Cmd_Argv( 1 ) ) )
1654 {
1655 SV_ClientPrintf( client->cl, PRINT_HIGH, "[MVD] Invalid password.\n" );
1656 return;
1657 }
1658 }
1659 MVD_StartAdmin( client );
1660 return;
1661 }
1662
1663 if( client->admin ) {
1664 MVD_ClientCommand( Cmd_RawString() );
1665 return;
1666 }
1667
1668 if( !Q_stricmp( cmd, "say" ) || !Q_stricmp( cmd, "say_team" ) ) {
1669 if( client->floodTime > sv.time ) {
1670 SV_ClientPrintf( client->cl, PRINT_HIGH,
1671 "[MVD] You can't talk for %d more seconds.\n",
1672 ( client->floodTime - sv.time ) / 1000 );
1673 return;
1674 }
1675 Cvar_ClampInteger( mvd_flood_msgs, 0, FLOOD_SAMPLES - 1 );
1676 i = client->floodHead - mvd_flood_msgs->integer - 1;
1677 if( i >= 0 ) {
1678 Cvar_ClampValue( mvd_flood_persecond, 0, 60 );
1679 if( sv.time - client->floodSamples[i & FLOOD_MASK] <
1680 mvd_flood_persecond->value * 1000 )
1681 {
1682 Cvar_ClampValue( mvd_flood_waitdelay, 0, 60 );
1683 SV_ClientPrintf( client->cl, PRINT_HIGH,
1684 "[MVD] You can't talk for %d seconds.\n",
1685 mvd_flood_waitdelay->integer );
1686 client->floodTime = sv.time + mvd_flood_waitdelay->value * 1000;
1687 return;
1688 }
1689 }
1690 SV_BroadcastPrintf( PRINT_CHAT, "[MVD] %s: %s\n", client->cl->name,
1691 Cmd_Args() );
1692 client->floodSamples[client->floodHead & FLOOD_MASK] = sv.time;
1693 client->floodHead++;
1694 return;
1695 }
1696 if( !Q_stricmp( cmd, "playernext" ) ) {
1697 MVD_GameFollowCycle( client, 1 );
1698 return;
1699 }
1700 if( !Q_stricmp( cmd, "playerprev" ) ) {
1701 MVD_GameFollowCycle( client, -1 );
1702 return;
1703 }
1704 if( !Q_stricmp( cmd, "playertoggle" ) ) {
1705 MVD_GameStartFollowing( client, client->savedClientNum );
1706 return;
1707 }
1708 if( !Q_stricmp( cmd, "follow" ) ) {
1709 if( Cmd_Argc() < 2 ) {
1710 if( !client->following ) {
1711 MVD_GameStartFollowing( client, client->savedClientNum );
1712 }
1713 return;
1714 }
1715 clientNum = atoi( Cmd_Argv( 1 ) );
1716 MVD_GameStartFollowing( client, clientNum );
1717 return;
1718 }
1719 if( !Q_stricmp( cmd, "observe" ) ) {
1720 if( client->following ) {
1721 MVD_GameStartObserving( client );
1722 }
1723 return;
1724 }
1725 if( !Q_stricmp( cmd, "inven" ) ) {
1726 if( client->scoreboard == SBOARD_CLIENTS ) {
1727 MVD_SetDefaultLayout( client );
1728 } else {
1729 client->scoreboard = SBOARD_CLIENTS;
1730 MVD_UpdateLayoutClients( client );
1731 }
1732 return;
1733 }
1734 if( !Q_stricmp( cmd, "help" ) ) {
1735 if( client->scoreboard == SBOARD_SCORES ) {
1736 MVD_SetDefaultLayout( client );
1737 } else {
1738 client->scoreboard = SBOARD_SCORES;
1739 MVD_UpdateLayoutScores( client );
1740 }
1741 return;
1742 }
1743
1744 if( !Q_stricmp( cmd, "putaway" ) ) {
1745 MVD_SetDefaultLayout( client );
1746 return;
1747 }
1748
1749 SV_ClientPrintf( client->cl, PRINT_LOW, "[MVD] unknown command '%s'\n", cmd );
1750 }
1751
MVD_Trace(vec3_t start,vec3_t mins,vec3_t maxs,vec3_t end)1752 trace_t MVD_Trace( vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end ) {
1753 return SV_Trace( start, mins, maxs, end, NULL, MASK_PLAYERSOLID );
1754 }
1755
MVD_GameClientThink(edict_t * ent,usercmd_t * cmd)1756 static void MVD_GameClientThink( edict_t *ent, usercmd_t *cmd ) {
1757 mvdClient_t *client;
1758 pmove_t pm;
1759 qboolean modeChange, playerChange;
1760
1761 client = ( mvdClient_t * )ent->client;
1762 if( client->admin && mvd.serverProtocol < PROTOCOL_VERSION_MVD ) {
1763 // VectorAdd( cmd->angles, client->delta_angles, mvd.cmd.angles );
1764 VectorCopy( cmd->angles, mvd.cmd.angles );
1765 mvd.cmd.forwardmove += cmd->forwardmove;
1766 mvd.cmd.sidemove += cmd->sidemove;
1767 mvd.cmd.upmove += cmd->upmove;
1768 mvd.cmd.buttons |= cmd->buttons;
1769 mvd.cmd.msec += cmd->msec;
1770 client->lastcmd = *cmd;
1771 return;
1772 }
1773 modeChange = !( client->lastcmd.buttons & BUTTON_ATTACK ) && ( cmd->buttons & BUTTON_ATTACK );
1774 playerChange = !client->lastcmd.upmove && cmd->upmove;
1775 client->lastcmd = *cmd;
1776
1777 if( modeChange ) {
1778 if( client->following ) {
1779 MVD_GameStartObserving( client );
1780 } else {
1781 MVD_GameStartFollowing( client, client->savedClientNum );
1782 }
1783 }
1784
1785 if( client->following ) {
1786 if( playerChange ) {
1787 MVD_GameFollowCycle( client, 1 );
1788 }
1789 return;
1790 }
1791
1792 memset( &pm, 0, sizeof( pm ) );
1793 pm.trace = MVD_Trace;
1794 pm.pointcontents = SV_PointContents;
1795 pm.s = client->ps.pmove;
1796 pm.cmd = *cmd;
1797
1798 PF_Pmove( &pm );
1799
1800 client->ps.pmove = pm.s;
1801 VectorCopy( pm.viewangles, client->ps.viewangles );
1802 }
1803
MVD_GameRunFrame(void)1804 static void MVD_GameRunFrame( void ) {
1805 client_t *client;
1806 mvdClient_t *mvdcl;
1807 mvdFrame_t *frames[2], *frame;
1808 playerStateEx_t *ps;
1809 int i, j;
1810
1811 if( mvd.demoplayback ) {
1812 /* run backwards, until we run out of buffers */
1813 if( mvd.demobackwards ) {
1814 MVD_SetPrevFrame();
1815 goto update;
1816 }
1817
1818 /* if paused, do not run forward */
1819 if( mvd_pause->integer ) {
1820 goto update;
1821 }
1822
1823 /* feed in next demo frame, if necessarry */
1824 while( mvd.demofile && mvd.activePacketNum >= mvd.serverPacketNum ) {
1825 if( !MVD_ParseNextMessage() ) {
1826 Com_Printf( "MVD finished, closing demofile.\n" );
1827 FS_FCloseFile( mvd.demofile );
1828 mvd.demofile = 0;
1829 if( mvd_nextserver->integer ) {
1830 SV_Nextserver();
1831 }
1832 return;
1833 }
1834 }
1835 }
1836
1837 if( mvd.state < MVD_CONNECTED ) {
1838 return;
1839 }
1840
1841 MVD_SetNextFrame();
1842
1843 update:
1844 frames[0] = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
1845 frames[1] = &mvd.frames[mvd.validPacketNum % mvd.frameBackup];
1846
1847 /* update clients */
1848 FOR_EACH_CLIENT( client ) {
1849 if( client->state < cs_spawned ) {
1850 continue;
1851 }
1852 if( client->protocol == PROTOCOL_VERSION_MVD ) {
1853 continue;
1854 }
1855 mvdcl = ( mvdClient_t * )client->edict->client;
1856 if( mvdcl->scoreboard == SBOARD_CLIENTS && mvdcl->layoutTime < sv.time && !mvdcl->admin ) {
1857 MVD_UpdateLayoutClients( mvdcl );
1858 }
1859 if( !mvdcl->following ) {
1860 continue;
1861 }
1862 frame = frames[mvdcl->admin];
1863 for( i = 0; i < frame->numPlayerStates; i++ ) {
1864 j = ( frame->firstPlayerState + i ) % mvd.maxPlayerStates;
1865 ps = &mvd.playerStates[j];
1866 if( ps->number == mvdcl->followClientNum ) {
1867 MVD_UpdateFollower( mvdcl, ps );
1868 break;
1869 }
1870 }
1871 if( i == frame->numPlayerStates ) {
1872 /* player not present in current frame */
1873 MVD_GameStartObserving( mvdcl );
1874 mvdcl->savedFollowing = qtrue;
1875 }
1876
1877 }
1878 }
1879
MVD_GameServerCommand(void)1880 static void MVD_GameServerCommand( void ) {
1881 }
1882
1883 game_export_t mvd_ge = {
1884 GAME_API_VERSION,
1885
1886 MVD_GameInit,
1887 MVD_GameShutdown,
1888
1889 MVD_GameSpawnEntities,
1890
1891 MVD_GameWriteGame,
1892 MVD_GameReadGame,
1893
1894 MVD_GameWriteLevel,
1895 MVD_GameReadLevel,
1896
1897 MVD_GameClientConnect,
1898 MVD_GameClientBegin,
1899 MVD_GameClientUserinfoChanged,
1900 MVD_GameClientDisconnect,
1901 MVD_GameClientCommand,
1902 MVD_GameClientThink,
1903
1904 MVD_GameRunFrame,
1905
1906 MVD_GameServerCommand,
1907
1908 ( edict_t * )mvd_edicts,
1909 sizeof( mvd_edicts[0] ),
1910 MAX_CLIENTS + 1,
1911 MAX_CLIENTS + 1
1912 };
1913
MVD_StreamedWrite(sizebuf_t * msgbuf,int bytesToSkip)1914 void MVD_StreamedWrite( sizebuf_t *msgbuf, int bytesToSkip ) {
1915 int len, swlen;
1916
1917 // the first several bytes are just packet sequencing stuff
1918 len = msgbuf->cursize - bytesToSkip;
1919 if( len < 1 ) {
1920 return; // don't write bad messages
1921 }
1922
1923 swlen = LittleLong( len );
1924 FS_Write( &swlen, 4, mvd.demofile );
1925 FS_Write( msgbuf->data + bytesToSkip, len, mvd.demofile );
1926 }
1927
MVD_StreamedStop_f(void)1928 void MVD_StreamedStop_f( void ) {
1929 int length;
1930
1931 if( !mvd.demorecording ) {
1932 Com_Printf( "Not recording a streamed demo.\n" );
1933 return;
1934 }
1935
1936 length = -1;
1937 FS_Write( &length, 4, mvd.demofile );
1938 FS_FCloseFile( mvd.demofile );
1939
1940 mvd.demofile = 0;
1941 mvd.demorecording = qfalse;
1942 mvd.demowaiting = qfalse;
1943
1944 Com_Printf( "Streamed demo recording completed.\n" );
1945 }
1946
MVD_StreamedRecord_f(void)1947 void MVD_StreamedRecord_f( void ) {
1948 mvdGamestate_t *gs;
1949 entityStateEx_t *ent;
1950 char *string;
1951 int i, j, length;
1952 char buffer[MAX_QPATH];
1953 char *name;
1954 fileHandle_t demofile;
1955 qboolean compressed = qfalse;
1956
1957 i = 1;
1958 string = Cmd_Argv( i );
1959 if( !strcmp( string, "-c" ) || !strcmp( string, "--compressed" ) ) {
1960 compressed = qtrue;
1961 i++;
1962 }
1963
1964 if( i >= Cmd_Argc() ) {
1965 Com_Printf( "Usage: %s [-c|--compressed] [/]<filename>\n",
1966 Cmd_Argv( 0 ) );
1967 return;
1968 }
1969
1970 if( mvd.demorecording ) {
1971 Com_Printf( "Already recording a streamed demo.\n" );
1972 return;
1973 }
1974
1975 if( mvd.state < MVD_CONNECTED || mvd.demoplayback ) {
1976 Com_Printf( "You must be connected to record.\n" );
1977 return;
1978 }
1979
1980 //
1981 // open the demo file
1982 //
1983 name = Cmd_Argv( i );
1984 if( name[0] == '/' ) {
1985 Q_strncpyz( buffer, name + 1, sizeof( buffer ) );
1986 } else {
1987 Com_sprintf( buffer, sizeof( buffer ), "demos/%s", name );
1988 switch( mvd.serverProtocol ) {
1989 case PROTOCOL_VERSION_MVD:
1990 string = ".mvd2";
1991 break;
1992 case PROTOCOL_VERSION_Q2PRO:
1993 string = ".dm_36";
1994 break;
1995 case PROTOCOL_VERSION_R1Q2:
1996 string = ".dm_35";
1997 break;
1998 default:
1999 string = ".dm2";
2000 break;
2001 }
2002 COM_DefaultExtension( buffer, string, sizeof( buffer ) );
2003 }
2004 if( compressed ) {
2005 Q_strcat( buffer, sizeof( buffer ), ".gz" );
2006 }
2007
2008 FS_FOpenFile( buffer, &demofile, FS_MODE_WRITE );
2009 if( !demofile ) {
2010 Com_EPrintf( "Couldn't open %s for writing\n", buffer );
2011 return;
2012 }
2013
2014 Com_Printf( "Recording streamed demo to %s\n", buffer );
2015 //Com_WPrintf( "Streamed demos are currently BROKEN!\n" );
2016
2017 mvd.demofile = demofile;
2018 mvd.demorecording = qtrue;
2019 mvd.demowaiting = qtrue;
2020
2021 gs = &mvd.gamestates[mvd.gamestateSequence & GAMESTATE_MASK];
2022
2023
2024 //
2025 // write out messages to hold the startup information
2026 //
2027
2028 // send the serverdata
2029 MSG_WriteByte( svc_serverdata );
2030 MSG_WriteLong( mvd.serverProtocol );
2031 MSG_WriteLong( 0x10000 + mvd.servercount );
2032 MSG_WriteByte( ATR_DEMO ); /* demos are always attract loops */
2033 string = Cvar_VariableString( "gamedir" );
2034 MSG_WriteString( string );
2035 MSG_WriteShort( mvd.clientNum );
2036 MSG_WriteString( gs->fullname );
2037
2038 // protocol-specific stuff
2039 switch( mvd.serverProtocol ) {
2040 case PROTOCOL_VERSION_R1Q2:
2041 MSG_WriteByte( 0 ); /* enhanced */
2042 MSG_WriteShort( PROTOCOL_VERSION_R1Q2_MINOR );
2043 MSG_WriteByte( 0 ); /* advanced deltas */
2044 MSG_WriteByte( 0 ); /* strafeHack */
2045 break;
2046 case PROTOCOL_VERSION_Q2PRO:
2047 MSG_WriteShort( PROTOCOL_VERSION_Q2PRO_MINOR );
2048 MSG_WriteByte( GT_DEATHMATCH );
2049 MSG_WriteByte( 0 ); /* strafeHack */
2050 MSG_WriteByte( 0 ); //atu QWMod
2051 break;
2052 case PROTOCOL_VERSION_MVD:
2053 MSG_WriteShort( PROTOCOL_VERSION_MVD_MINOR );
2054 break;
2055 default:
2056 break;
2057 }
2058
2059 // configstrings
2060 for( i = 0; i < MAX_CONFIGSTRINGS; i++ ) {
2061 string = mvd.configstrings[i];
2062 if( !string || !string[0] ) {
2063 continue;
2064 }
2065
2066 length = strlen( string );
2067 if( length > MAX_QPATH ) {
2068 length = MAX_QPATH;
2069 }
2070
2071 if( mvd.serverProtocol < PROTOCOL_VERSION_Q2PRO ) {
2072 if( msg_write.cursize + length + 4 > MAX_PACKETLEN ) {
2073 MVD_StreamedWrite( &msg_write, 0 );
2074 SZ_Clear( &msg_write );
2075 }
2076 }
2077 MSG_WriteByte( svc_configstring );
2078 MSG_WriteShort( i );
2079 MSG_WriteData( string, length );
2080 MSG_WriteByte( 0 );
2081 }
2082
2083 // baselines
2084 for( i = 0; i < SV_BASELINES_CHUNKS; i++ ) {
2085 if( !gs->baselines[i] ) {
2086 continue;
2087 }
2088 for( j = 0; j < SV_BASELINES_PER_CHUNK; j++ ) {
2089 ent = &gs->baselines[i][j];
2090 if( !ent->s.number ) {
2091 continue;
2092 }
2093 if( mvd.serverProtocol < PROTOCOL_VERSION_Q2PRO ) {
2094 if( msg_write.cursize + 64 > MAX_PACKETLEN ) {
2095 MVD_StreamedWrite( &msg_write, 0 );
2096 SZ_Clear( &msg_write );
2097 }
2098 }
2099 MSG_WriteByte( svc_spawnbaseline );
2100 MSG_WriteDeltaEntity( NULL, &ent->s, MSG_ES_FORCE );
2101 }
2102 }
2103
2104 MSG_WriteByte( svc_stufftext );
2105 MSG_WriteString( "precache\n" );
2106
2107 MVD_StreamedWrite( &msg_write, 0 );
2108 SZ_Clear( &msg_write );
2109 }
2110
MVD_Jump_f(void)2111 void MVD_Jump_f( void ) {
2112 int percent, delta, savedPacketNum;
2113 mvdFrame_t *frame;
2114 mvdClient_t *gclient;
2115 client_t *client;
2116
2117 if( !mvd.demoplayback ) {
2118 Com_Printf( "Not playing back a MVD.\n" );
2119 return;
2120 }
2121
2122 if( Cmd_Argc() < 2 ) {
2123 Com_Printf( "Usage: %s <percent>\n", Cmd_Argv( 0 ) );
2124 return;
2125 }
2126
2127 percent = atoi( Cmd_Argv( 1 ) );
2128 if( percent < 0 || percent > 100 ) {
2129 Com_Printf( "Percent should be in the [0, 100] range\n" );
2130 return;
2131 }
2132
2133 savedPacketNum = mvd.activePacketNum;
2134
2135 delta = ( 100 - percent ) * ( mvd.frameBackup - 1 ) / 100;
2136 mvd.activePacketNum = mvd.serverPacketNum - delta - 1;
2137 if( mvd.activePacketNum < 0 ) {
2138 mvd.activePacketNum = 0;
2139 }
2140
2141 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
2142
2143 while( mvd.activePacketNum < mvd.serverPacketNum ) {
2144 mvd.activePacketNum++;
2145 frame = &mvd.frames[mvd.activePacketNum % mvd.frameBackup];
2146
2147 if( frame->serverPacketNum != mvd.activePacketNum ) {
2148 MVD_DPrintf( "MVD_Jump_f: %d: never received\n",
2149 mvd.activePacketNum );
2150 continue;
2151 }
2152
2153 /* got new gamestate? */
2154 if( frame->gamestateSequence != mvd.activeGamestateSequence ) {
2155 MVD_DPrintf( "MVD_Jump_f: %d: different gamestateSequence\n",
2156 mvd.activePacketNum );
2157 continue;
2158 }
2159
2160 /* does this frame contain svc_frame? */
2161 if( !( frame->flags & MFF_ACTIVE ) ) {
2162 continue;
2163 }
2164
2165 if( !( frame->flags & MFF_VALID ) ) {
2166 MVD_DPrintf( "MVD_Jump_f: %d: invalid frame\n",
2167 mvd.activePacketNum );
2168 continue;
2169 }
2170
2171 if( mvd.nextEntityStates - frame->firstEntityState >
2172 mvd.maxEntityStates )
2173 {
2174 MVD_DPrintf( "MVD_Jump_f: %d: entityStates too old\n",
2175 mvd.activePacketNum );
2176 continue;
2177 }
2178 if( mvd.nextPlayerStates - frame->firstPlayerState >
2179 mvd.maxPlayerStates )
2180 {
2181 MVD_DPrintf( "MVD_Jump_f: %d: playerStates too old\n",
2182 mvd.activePacketNum );
2183 continue;
2184 }
2185
2186 if( mvd.nextMessageBytes - frame->firstMessageByte >
2187 mvd.maxMessageBytes )
2188 {
2189 MVD_DPrintf( "MVD_Jump_f: %d: messages too old\n",
2190 mvd.activePacketNum );
2191 continue;
2192 }
2193
2194 break;
2195 }
2196
2197 if( mvd.activePacketNum == savedPacketNum ) {
2198 return;
2199 }
2200
2201 /* advance server state to this frame */
2202 mvd.prevPacketNum = mvd.activePacketNum;
2203 MVD_TransitionFrame( frame );
2204
2205 /*for( i = 1; i < ge->num_edicts; i++, ent++ ) {
2206 ent = EDICT_NUM( i );
2207 ent->s.event = EV_OTHER_TELEPORT;
2208 if( !( ent->s.renderfx & RF_BEAM ) ) {
2209 VectorCopy( ent->s.origin, ent->s.old_origin );
2210 }
2211 }*/
2212
2213 FOR_EACH_CLIENT( client ) {
2214 if( client->state < cs_spawned ) {
2215 continue;
2216 }
2217 if( client->protocol == PROTOCOL_VERSION_MVD ) {
2218 continue;
2219 }
2220 gclient = ( mvdClient_t * )client->edict->client;
2221 if( !gclient->following ) {
2222 continue;
2223 }
2224 gclient->pmflags ^= PMF_TELEPORT_BIT;
2225 }
2226 }
2227
2228 #ifndef DEDICATED_ONLY
2229 /* called by the client code */
MVD_GetDemoPercent(int * percent,int * bufferPercent)2230 qboolean MVD_GetDemoPercent( int *percent, int *bufferPercent ) {
2231 int delta;
2232
2233 if( !mvd.demoplayback ) {
2234 return qfalse;
2235 }
2236
2237 delta = mvd.serverPacketNum - mvd.activePacketNum;
2238 *bufferPercent = 100 - delta * 100 / ( mvd.frameBackup - 1 );
2239 *percent = mvd.demofilePercent;
2240
2241 return qtrue;
2242 }
2243 #endif
2244
MVD_Play_g(const char * partial,int state)2245 const char *MVD_Play_g( const char *partial, int state ) {
2246 return Com_FileNameGeneratorByFilter( "demos", "*.mvd2;*.mvd2.gz", partial, qfalse, state );
2247 }
2248
MVD_Play_f(void)2249 void MVD_Play_f( void ) {
2250 char *name;
2251 char buffer[MAX_QPATH];
2252 fileHandle_t f;
2253 int length;
2254
2255 if( Cmd_Argc() < 2 ) {
2256 Com_Printf( "Usage: %s [/]<filename>\n", Cmd_Argv( 0 ) );
2257 return;
2258 }
2259
2260 name = Cmd_Argv( 1 );
2261 if( name[0] == '/' ) {
2262 Q_strncpyz( buffer, name + 1, sizeof( buffer ) );
2263 } else {
2264 Com_sprintf( buffer, sizeof( buffer ), "demos/%s", name );
2265 COM_DefaultExtension( buffer, ".mvd2", sizeof( buffer ) );
2266 }
2267 FS_FOpenFile( buffer, &f, FS_MODE_READ );
2268 if( !f ) {
2269 Com_Printf( "Couldn't open '%s'\n", buffer );
2270 return;
2271 }
2272
2273 if( !MVD_ReadNextMessage( f ) ) {
2274 Com_Printf( "Couldn't read the first message from '%s'.\n"
2275 "Demo is possibly broken or truncated.\n", buffer );
2276 FS_FCloseFile( f );
2277 return;
2278 }
2279
2280 SV_Shutdown( "Server restarted\n", KILL_RESTART );
2281
2282 mvd.demofile = f;
2283 mvd.demoplayback = qtrue;
2284 mvd.state = MVD_CONNECTED;
2285
2286 mvd.serverPacketNum++;
2287 MVD_ParseMessage();
2288
2289 do {
2290 if( !MVD_ParseNextMessage() ) {
2291 Com_WPrintf( "Premature end of MVD.\n" );
2292 SV_Nextserver();
2293 return;
2294 }
2295 MVD_SetNextFrame();
2296 if( mvd.state < MVD_CONNECTED ) {
2297 return;
2298 }
2299 } while( mvd.state < MVD_ACTIVE );
2300
2301 if( dedicated->integer && !sv_nextserver->string[0] ) {
2302 Cvar_Set( "nextserver", va( "mvdplay /%s", buffer ) );
2303 }
2304
2305 length = FS_GetFileLengthNoCache( mvd.demofile );
2306 mvd.demofileFrameOffset = FS_Tell( mvd.demofile );
2307 mvd.demofileSize = length - mvd.demofileFrameOffset;
2308 strcpy( mvd.demopath, buffer );
2309 }
2310
MVD_ConnectionlessPacket(void)2311 static void MVD_ConnectionlessPacket( void ) {
2312 char *c, *s;
2313 netchan_t *netchan;
2314 netchan_type_t type;
2315 int i, j, k;
2316
2317 MSG_BeginReading();
2318 MSG_ReadLong(); // skip the -1
2319
2320 s = MSG_ReadStringLine();
2321
2322 Cmd_TokenizeString( s, qfalse );
2323
2324 c = Cmd_Argv( 0 );
2325
2326 Com_DPrintf( "[MVD] ClientPacket: %s: %s\n", NET_AdrToString( &net_from ), s );
2327
2328 /* challenge from the server we are connecting to */
2329 if ( !strcmp( c, "challenge" ) ) {
2330 qboolean proto35 = qfalse, proto36 = qfalse, proto37 = qfalse;
2331
2332 if ( mvd.state < MVD_CHALLENGING ) {
2333 Com_DPrintf( "[MVD] Challenge received while not connecting. Ignored.\n" );
2334 return;
2335 }
2336 if ( !NET_IsEqualBaseAdr( &net_from, &mvd.serverAddress ) ) {
2337 Com_DPrintf( "[MVD] Challenge from different address. Ignored.\n" );
2338 return;
2339 }
2340 if ( mvd.state > MVD_CHALLENGING ) {
2341 Com_DPrintf( "[MVD] Dup challenge received. Ignored.\n" );
2342 return;
2343 }
2344
2345 /* parse additional parameters */
2346 j = Cmd_Argc();
2347 for( i = 2; i < j; i++ ) {
2348 s = Cmd_Argv( i );
2349 if( !strncmp( s, "p=", 2 ) ) {
2350 s += 2;
2351 while( *s ) {
2352 k = atoi( s );
2353 if( k == PROTOCOL_VERSION_R1Q2 ) {
2354 proto35 = qtrue;
2355 } else if( k == PROTOCOL_VERSION_Q2PRO ) {
2356 proto36 = qtrue;
2357 } else if( k == PROTOCOL_VERSION_MVD ) {
2358 proto37 = qtrue;
2359 }
2360 s = strchr( s, ',' );
2361 if( s == NULL ) {
2362 break;
2363 }
2364 s++;
2365 }
2366 }
2367 }
2368
2369 /* select the 'best' protocol available, unless told otherwise */
2370 if( mvd.serverProtocol == 0 ||
2371 ( mvd.serverProtocol == PROTOCOL_VERSION_R1Q2 && !proto35 ) ||
2372 ( mvd.serverProtocol == PROTOCOL_VERSION_Q2PRO && !proto36 ) ||
2373 ( mvd.serverProtocol == PROTOCOL_VERSION_MVD && !proto37 ) )
2374 {
2375 if( proto37 ) {
2376 mvd.serverProtocol = PROTOCOL_VERSION_MVD;
2377 } else if( proto36 ) {
2378 mvd.serverProtocol = PROTOCOL_VERSION_Q2PRO;
2379 } else if( proto35 ) {
2380 mvd.serverProtocol = PROTOCOL_VERSION_R1Q2;
2381 } else {
2382 mvd.serverProtocol = PROTOCOL_VERSION_DEFAULT;
2383 }
2384 }
2385 Com_DPrintf( "[MVD] Selected protocol %d\n", mvd.serverProtocol );
2386
2387 mvd.challenge = atoi( Cmd_Argv( 1 ) );
2388 mvd.state = MVD_CONNECTING;
2389 mvd.connectTime = -9999;
2390 mvd.connectCount = 0;
2391 return;
2392 }
2393
2394 if ( !strcmp( c, "client_connect" ) ) {
2395 if ( mvd.state < MVD_CONNECTING ) {
2396 Com_DPrintf( "[MVD] Connect received while not connecting. Ignored.\n" );
2397 return;
2398 }
2399 if ( !NET_IsEqualBaseAdr( &net_from, &mvd.serverAddress ) ) {
2400 Com_DPrintf( "[MVD] Connect from different address. Ignored.\n" );
2401 return;
2402 }
2403 if ( mvd.state > MVD_CONNECTING ) {
2404 Com_DPrintf( "[MVD] Dup connect received. Ignored.\n" );
2405 return;
2406 }
2407
2408 if( mvd.serverProtocol < PROTOCOL_VERSION_Q2PRO ) {
2409 type = NETCHAN_OLD;
2410 } else {
2411 type = NETCHAN_NEW;
2412 }
2413
2414 /* parse additional parameters */
2415 j = Cmd_Argc();
2416 for( i = 1; i < j; i++ ) {
2417 s = Cmd_Argv( i );
2418 if( !strncmp( s, "nc=", 3 ) ) {
2419 s += 3;
2420 if( *s ) {
2421 type = atoi( s );
2422 if( type != NETCHAN_OLD && type != NETCHAN_NEW ) {
2423 Com_Error( ERR_DISCONNECT,
2424 "[MVD] Server returned invalid netchan type" );
2425 }
2426 }
2427 }
2428 }
2429
2430 netchan = Netchan_Setup( NS_CLIENT, type, &mvd.serverAddress,
2431 mvd.quakePort, 1024, mvd.serverProtocol );
2432
2433 Com_Printf( "[MVD] Connection to %s established (protocol %d).\n",
2434 NET_AdrToString( &netchan->remote_address ), mvd.serverProtocol );
2435
2436 mvd.state = MVD_CONNECTED;
2437 mvd.netchan = netchan;
2438 MVD_ClientCommand( "new" );
2439 return;
2440 }
2441
2442 if ( !strcmp( c, "print" ) ) {
2443 if ( ( mvd.state == MVD_CHALLENGING || mvd.state == MVD_CONNECTING ) &&
2444 NET_IsEqualBaseAdr( &net_from, &mvd.serverAddress ) )
2445 {
2446 s = MSG_ReadString();
2447 Com_Error( ERR_DISCONNECT, "[MVD] %s", s );
2448 }
2449 return;
2450 }
2451 }
2452
MVD_PacketEvent(int ret)2453 void MVD_PacketEvent( int ret ) {
2454 int bytesToSkip;
2455
2456 if( !mvd_running->integer ) {
2457 return;
2458 }
2459
2460 //
2461 // remote command packet
2462 //
2463 if ( ret == 1 && *( int * )msg_read.data == -1 ) {
2464 MVD_ConnectionlessPacket();
2465 return;
2466 }
2467
2468 if ( mvd.state < MVD_CONNECTED )
2469 return; // dump it if not connected
2470
2471 if ( ret == 1 && msg_read.cursize < 8 ) {
2472 Com_DPrintf( "[MVD] %s: runt packet\n", NET_AdrToString( &net_from ) );
2473 return;
2474 }
2475
2476 if ( !mvd.netchan )
2477 return;
2478
2479 //
2480 // packet from server
2481 //
2482 if ( !NET_IsEqualAdr( &net_from, &mvd.netchan->remote_address ) ) {
2483 Com_DPrintf( "[MVD] %s: sequenced packet without connection\n",
2484 NET_AdrToString( &net_from ) );
2485 return;
2486 }
2487
2488 if( ret == -1 ) {
2489 Com_Error( ERR_DISCONNECT, "[MVD] Connection reset by peer" );
2490 }
2491
2492 if ( !mvd.netchan->Process( mvd.netchan ) )
2493 return; // wasn't accepted for some reason
2494
2495 bytesToSkip = msg_read.readcount;
2496
2497 mvd.serverPacketNum = mvd.netchan->incoming_sequence;
2498 MVD_ParseMessage();
2499
2500 if( mvd.demorecording && !mvd.demowaiting ) {
2501 MVD_StreamedWrite( &msg_read, bytesToSkip );
2502 }
2503 }
2504
MVD_CheckForResend(void)2505 void MVD_CheckForResend( void ) {
2506 char tail[32];
2507 int ret;
2508
2509 if( mvd.realtime - mvd.connectTime < 3000 ) {
2510 return;
2511 }
2512 mvd.connectTime = mvd.realtime;
2513 mvd.connectCount++;
2514
2515 if( mvd.state < MVD_CONNECTING ) {
2516 Com_Printf( "[MVD] Requesting challenge... %i\n", mvd.connectCount );
2517 ret = Netchan_OutOfBandPrint( NS_CLIENT, &mvd.serverAddress,
2518 "getchallenge\n" );
2519 if( ret == -1 ) {
2520 Com_Error( ERR_DISCONNECT, "[MVD] %s to %s\n", Sys_NetErrorString(),
2521 NET_AdrToString( &mvd.serverAddress ) );
2522 }
2523 return;
2524 }
2525
2526 Com_Printf( "[MVD] Requesting connection... %i\n", mvd.connectCount );
2527
2528 mvd.quakePort = net_qport->integer;
2529 if( mvd.serverProtocol != PROTOCOL_VERSION_DEFAULT ) {
2530 Com_sprintf( tail, sizeof( tail ), " %d",
2531 net_maxmsglen->integer );
2532 if( mvd.serverProtocol >= PROTOCOL_VERSION_Q2PRO ) {
2533 strcat( tail, net_chantype->integer ? " 1" : " 0" );
2534 }
2535 mvd.quakePort &= 0xFF;
2536 } else {
2537 tail[0] = 0;
2538 }
2539
2540 ret = Netchan_OutOfBandPrint( NS_CLIENT, &mvd.serverAddress,
2541 "connect %i %i %i \"%s\"%s\n", mvd.serverProtocol, mvd.quakePort,
2542 mvd.challenge, Cvar_Userinfo(), tail );
2543 if( ret == -1 ) {
2544 Com_Error( ERR_DISCONNECT, "[MVD] %s to %s\n", Sys_NetErrorString(),
2545 NET_AdrToString( &mvd.serverAddress ) );
2546 }
2547 }
2548
MVD_ClientFrame(int msec)2549 void MVD_ClientFrame( int msec ) {
2550 int checksumIndex;
2551
2552 if( !mvd.state ) {
2553 return;
2554 }
2555
2556 Cbuf_ExecuteEx( &mvd_buffer );
2557
2558 mvd.realtime += msec;
2559
2560 if( mvd.state < MVD_CONNECTED ) {
2561 MVD_CheckForResend();
2562 return;
2563 }
2564
2565 if( !mvd.netchan ) {
2566 return;
2567 }
2568
2569 //
2570 // check timeout
2571 //
2572 if( Sys_Milliseconds() - mvd.netchan->last_received > mvd_timeout->value * 1000 ) {
2573 // timeoutcount saves debugger
2574 if ( ++mvd.timeoutcount > 5 ) {
2575 Com_Error( ERR_DISCONNECT, "[MVD] Server connection timed out." );
2576 }
2577 } else {
2578 mvd.timeoutcount = 0;
2579 }
2580
2581 if( cvar_infoModified & CVAR_USERINFO ) {
2582 MSG_WriteByte( clc_userinfo );
2583 MSG_WriteString( Cvar_Userinfo() );
2584 MSG_FlushTo( &mvd.netchan->message );
2585 Com_DPrintf( "[MVD] Sending userinfo update\n" );
2586 }
2587
2588 if( mvd.state < MVD_BUFFERING ) {
2589 if( mvd.netchan->ShouldUpdate( mvd.netchan ) ) {
2590 mvd.netchan->Transmit( mvd.netchan, 0, NULL );
2591 }
2592 return;
2593 }
2594
2595 if( mvd.realtime - mvd.sendtime < 33 &&
2596 !mvd.netchan->ShouldUpdate( mvd.netchan ) )
2597 {
2598 return;
2599 }
2600
2601 if( !mvd.cmd.msec ) {
2602 mvd.cmd.msec = mvd.realtime - mvd.sendtime;
2603 }
2604 if( mvd.cmd.msec > 250 ) {
2605 mvd.cmd.msec = 100;
2606 }
2607
2608 MSG_WriteByte( clc_move );
2609
2610 // save the position for a checksum byte
2611 checksumIndex = 0;
2612 if( mvd.serverProtocol == PROTOCOL_VERSION_DEFAULT ) {
2613 checksumIndex = msg_write.cursize;
2614 SZ_GetSpace( &msg_write, 1 );
2615 }
2616 if( mvd.validPacketNum != mvd.serverPacketNum || mvd.demowaiting ) {
2617 MSG_WriteLong( -1 );
2618 } else {
2619 MSG_WriteLong( mvd.lastServerFrame );
2620 }
2621
2622 MSG_WriteDeltaUsercmd( NULL, &mvd.cmd );
2623 MSG_WriteByte( 0 );
2624
2625 MSG_WriteByte( 0 );
2626 MSG_WriteByte( mvd.cmd.msec );
2627 MSG_WriteByte( 0 );
2628
2629 MSG_WriteByte( 0 );
2630 MSG_WriteByte( mvd.cmd.msec );
2631 MSG_WriteByte( 0 );
2632
2633 if( mvd.serverProtocol == PROTOCOL_VERSION_DEFAULT ) {
2634 // calculate a checksum over the move commands
2635 msg_write.data[checksumIndex] = COM_BlockSequenceCRCByte(
2636 msg_write.data + checksumIndex + 1,
2637 msg_write.cursize - checksumIndex - 1,
2638 mvd.netchan->outgoing_sequence );
2639 }
2640
2641 mvd.cmd.forwardmove = 0;
2642 mvd.cmd.sidemove = 0;
2643 mvd.cmd.upmove = 0;
2644 mvd.cmd.buttons = 0;
2645 mvd.cmd.msec = 0;
2646
2647 mvd.sendtime = mvd.realtime;
2648
2649 mvd.netchan->Transmit( mvd.netchan, msg_write.cursize, msg_write.data );
2650
2651 //Com_Printf( "Sent packet\n" );
2652
2653 SZ_Clear( &msg_write );
2654
2655 }
2656
MVD_Connect_f(void)2657 void MVD_Connect_f( void ) {
2658 netadr_t adr;
2659 char *s;
2660 int protocol;
2661
2662 if ( Cmd_Argc() < 2 ) {
2663 usage:
2664 Com_Printf( "Usage: %s <server> [protocol]\n"
2665 "Supported protocols: %d, %d, %d and %d\n",
2666 Cmd_Argv( 0 ),
2667 PROTOCOL_VERSION_DEFAULT,
2668 PROTOCOL_VERSION_R1Q2,
2669 PROTOCOL_VERSION_Q2PRO,
2670 PROTOCOL_VERSION_MVD );
2671 return;
2672 }
2673
2674 protocol = 0;
2675 if( Cmd_Argc() > 2 ) {
2676 protocol = atoi( Cmd_Argv( 2 ) );
2677 if( protocol < PROTOCOL_VERSION_DEFAULT ||
2678 protocol > PROTOCOL_VERSION_MVD )
2679 {
2680 goto usage;
2681 }
2682 }
2683
2684 s = Cmd_Argv( 1 );
2685 if( !NET_StringToAdr( s, &adr ) ) {
2686 Com_Printf( "Bad server address: %s\n", s );
2687 return;
2688 }
2689 if( !adr.port ) {
2690 adr.port = BigShort( PORT_SERVER );
2691 }
2692
2693 SV_Shutdown( "Server restarted\n", KILL_RESTART );
2694
2695 NET_Config( NET_CLIENT );
2696
2697 Cvar_SetInteger( "mvd_running", 1 );
2698
2699 mvd.serverAddress = adr;
2700 mvd.serverProtocol = protocol;
2701 mvd.connectCount = 0;
2702 mvd.connectTime = -9999;
2703 mvd.state = MVD_CHALLENGING;
2704 }
2705
MVD_Disconnect_f(void)2706 void MVD_Disconnect_f( void ) {
2707 if( mvd.state ) {
2708 Com_Error( ERR_SILENT, "[MVD] Disconnected from server.\n" );
2709 }
2710 Com_Printf( "Not connected to a MVD server.\n" );
2711 }
2712
MVD_Disconnect(void)2713 void MVD_Disconnect( void ) {
2714 int i;
2715 mvdGamestate_t *gs;
2716
2717 if( mvd.demorecording ) {
2718 MVD_StreamedStop_f();
2719 }
2720
2721 if( mvd.netchan ) {
2722 MSG_WriteByte( clc_stringcmd );
2723 MSG_WriteString( "disconnect" );
2724
2725 mvd.netchan->Transmit( mvd.netchan, msg_write.cursize, msg_write.data );
2726 mvd.netchan->Transmit( mvd.netchan, msg_write.cursize, msg_write.data );
2727 mvd.netchan->Transmit( mvd.netchan, msg_write.cursize, msg_write.data );
2728
2729 SZ_Clear( &msg_write );
2730
2731 Netchan_Close( mvd.netchan );
2732 }
2733
2734 if( mvd.clients ) {
2735 Z_Free( mvd.clients );
2736 }
2737
2738 Z_FreeTags( TAG_MVD );
2739 if( mvd.demofile ) {
2740 FS_FCloseFile( mvd.demofile );
2741 }
2742
2743 for( i = 0; i < MAX_GAMESTATES; i++ ) {
2744 gs = &mvd.gamestates[i];
2745 if( gs->cm.cache ) {
2746 CM_FreeMap( &gs->cm );
2747 }
2748 }
2749
2750 if( mvd.state ) {
2751 memset( &mvd, 0, sizeof( mvd ) );
2752 }
2753
2754 Cvar_SetInteger( "mvd_running", 0 );
2755 }
2756
2757
2758
2759