diff options
author | Tim Angus <tim@ngus.net> | 2006-11-28 23:46:04 +0000 |
---|---|---|
committer | Tim Angus <tim@ngus.net> | 2006-11-28 23:46:04 +0000 |
commit | 92905745a71578eed267f25311d7a18d49976f6d (patch) | |
tree | 5836f0a4424b938c6782190ec9c28c6be808f389 /src/client | |
parent | 6af24abf02f1d0dedc5dbe655a37d5afc9f0646a (diff) |
* Merge ioq3-989
Diffstat (limited to 'src/client')
-rw-r--r-- | src/client/cl_avi.c | 8 | ||||
-rw-r--r-- | src/client/cl_cgame.c | 2 | ||||
-rw-r--r-- | src/client/cl_console.c | 4 | ||||
-rw-r--r-- | src/client/cl_curl.c | 358 | ||||
-rw-r--r-- | src/client/cl_curl.h | 101 | ||||
-rw-r--r-- | src/client/cl_keys.c | 3 | ||||
-rw-r--r-- | src/client/cl_main.c | 131 | ||||
-rw-r--r-- | src/client/cl_parse.c | 32 | ||||
-rw-r--r-- | src/client/cl_ui.c | 6 | ||||
-rw-r--r-- | src/client/client.h | 19 | ||||
-rw-r--r-- | src/client/qal.c | 4 | ||||
-rw-r--r-- | src/client/snd_codec_ogg.c | 8 | ||||
-rw-r--r-- | src/client/snd_main.c | 4 | ||||
-rw-r--r-- | src/client/snd_openal.c | 406 |
14 files changed, 954 insertions, 132 deletions
diff --git a/src/client/cl_avi.c b/src/client/cl_avi.c index 463f873c..c54e7054 100644 --- a/src/client/cl_avi.c +++ b/src/client/cl_avi.c @@ -2,20 +2,20 @@ =========================================================================== Copyright (C) 2005-2006 Tim Angus -This file is part of Quake III Arena source code. +This file is part of Tremulous. -Quake III Arena source code is free software; you can redistribute it +Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. -Quake III Arena source code is distributed in the hope that it will be +Tremulous is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software +along with Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ diff --git a/src/client/cl_cgame.c b/src/client/cl_cgame.c index fd7c29ed..262f6b47 100644 --- a/src/client/cl_cgame.c +++ b/src/client/cl_cgame.c @@ -981,7 +981,7 @@ void CL_SetCGameTime( void ) { } // allow pause in single player - if ( sv_paused->integer && cl_paused->integer && com_sv_running->integer ) { + if ( sv_paused->integer && CL_CheckPaused() && com_sv_running->integer ) { // paused return; } diff --git a/src/client/cl_console.c b/src/client/cl_console.c index 22a148a1..3e321394 100644 --- a/src/client/cl_console.c +++ b/src/client/cl_console.c @@ -516,13 +516,13 @@ void Con_DrawSolidConsole( float frac ) { re.SetColor( g_color_table[ColorIndex(COLOR_RED)] ); - i = strlen( Q3_VERSION ); + i = strlen( SVN_VERSION ); for (x=0 ; x<i ; x++) { SCR_DrawSmallChar( cls.glconfig.vidWidth - ( i - x ) * SMALLCHAR_WIDTH, - (lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), Q3_VERSION[x] ); + (lines-(SMALLCHAR_HEIGHT+SMALLCHAR_HEIGHT/2)), SVN_VERSION[x] ); } diff --git a/src/client/cl_curl.c b/src/client/cl_curl.c new file mode 100644 index 00000000..246095a1 --- /dev/null +++ b/src/client/cl_curl.c @@ -0,0 +1,358 @@ +/* +=========================================================================== +Copyright (C) 2006 Tony J. White (tjw@tjw.org) + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#if USE_CURL +#include "client.h" +cvar_t *cl_cURLLib; + +#if USE_CURL_DLOPEN + +#if USE_SDL_VIDEO +#include "SDL.h" +#include "SDL_loadso.h" +#define OBJTYPE void * +#define OBJLOAD(x) SDL_LoadObject(x) +#define SYMLOAD(x,y) SDL_LoadFunction(x,y) +#define OBJFREE(x) SDL_UnloadObject(x) + +#elif defined _WIN32 +#include <windows.h> +#define OBJTYPE HMODULE +#define OBJLOAD(x) LoadLibrary(x) +#define SYMLOAD(x,y) GetProcAddress(x,y) +#define OBJFREE(x) FreeLibrary(x) + +#elif defined __linux__ || defined __FreeBSD__ || defined MACOS_X || defined __sun +#include <dlfcn.h> +#define OBJTYPE void * +#define OBJLOAD(x) dlopen(x, RTLD_LAZY | RTLD_GLOBAL) +#define SYMLOAD(x,y) dlsym(x,y) +#define OBJFREE(x) dlclose(x) +#else + +#error "Your platform has no lib loading code or it is disabled" +#endif + +#if defined __linux__ || defined __FreeBSD__ || defined MACOS_X +#include <unistd.h> +#include <sys/types.h> +#endif + +char* (*qcurl_version)(void); + +CURL* (*qcurl_easy_init)(void); +CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); +CURLcode (*qcurl_easy_perform)(CURL *curl); +void (*qcurl_easy_cleanup)(CURL *curl); +CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); +CURL* (*qcurl_easy_duphandle)(CURL *curl); +void (*qcurl_easy_reset)(CURL *curl); +const char *(*qcurl_easy_strerror)(CURLcode); + +CURLM* (*qcurl_multi_init)(void); +CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, + CURL *curl_handle); +CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, + CURL *curl_handle); +CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, + int *running_handles); +CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); +CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, + int *msgs_in_queue); +const char *(*qcurl_multi_strerror)(CURLMcode); + +static OBJTYPE cURLLib = NULL; + +/* +================= +GPA +================= +*/ +static void *GPA(char *str) +{ + void *rv; + + rv = SYMLOAD(cURLLib, str); + if(!rv) + { + Com_Printf("Can't load symbol %s\n", str); + clc.cURLEnabled = qfalse; + return NULL; + } + else + { + Com_DPrintf("Loaded symbol %s (0x%08X)\n", str, rv); + return rv; + } +} +#endif /* USE_CURL_DLOPEN */ + +/* +================= +CL_cURL_Init +================= +*/ +qboolean CL_cURL_Init() +{ +#if USE_CURL_DLOPEN + if(cURLLib) + return qtrue; + + + Com_Printf("Loading \"%s\"...", cl_cURLLib->string); + if( (cURLLib = OBJLOAD(cl_cURLLib->string)) == 0 ) + { +#ifdef _WIN32 + return qfalse; +#else + char fn[1024]; + getcwd(fn, sizeof(fn)); + strncat(fn, "/", sizeof(fn)); + strncat(fn, cl_cURLLib->string, sizeof(fn)); + + if( (cURLLib = OBJLOAD(fn)) == 0 ) + { + return qfalse; + } +#endif /* _WIN32 */ + } + + clc.cURLEnabled = qtrue; + + qcurl_version = GPA("curl_version"); + + qcurl_easy_init = GPA("curl_easy_init"); + qcurl_easy_setopt = GPA("curl_easy_setopt"); + qcurl_easy_perform = GPA("curl_easy_perform"); + qcurl_easy_cleanup = GPA("curl_easy_cleanup"); + qcurl_easy_getinfo = GPA("curl_easy_getinfo"); + qcurl_easy_duphandle = GPA("curl_easy_duphandle"); + qcurl_easy_reset = GPA("curl_easy_reset"); + qcurl_easy_strerror = GPA("curl_easy_strerror"); + + qcurl_multi_init = GPA("curl_multi_init"); + qcurl_multi_add_handle = GPA("curl_multi_add_handle"); + qcurl_multi_remove_handle = GPA("curl_multi_remove_handle"); + qcurl_multi_fdset = GPA("curl_multi_fdset"); + qcurl_multi_perform = GPA("curl_multi_perform"); + qcurl_multi_cleanup = GPA("curl_multi_cleanup"); + qcurl_multi_info_read = GPA("curl_multi_info_read"); + qcurl_multi_strerror = GPA("curl_multi_strerror"); + + if(!clc.cURLEnabled) + { + CL_cURL_Shutdown(); + Com_Printf("FAIL One or more symbols not found\n"); + return qfalse; + } + Com_Printf("OK\n"); + + return qtrue; +#else + clc.cURLEnabled = qtrue; + return qtrue; +#endif /* USE_CURL_DLOPEN */ +} + +/* +================= +CL_cURL_Shutdown +================= +*/ +void CL_cURL_Shutdown( void ) +{ + CL_cURL_Cleanup(); +#if USE_CURL_DLOPEN + if(cURLLib) + { + OBJFREE(cURLLib); + cURLLib = NULL; + } + qcurl_easy_init = NULL; + qcurl_easy_setopt = NULL; + qcurl_easy_perform = NULL; + qcurl_easy_cleanup = NULL; + qcurl_easy_getinfo = NULL; + qcurl_easy_duphandle = NULL; + qcurl_easy_reset = NULL; + + qcurl_multi_init = NULL; + qcurl_multi_add_handle = NULL; + qcurl_multi_remove_handle = NULL; + qcurl_multi_fdset = NULL; + qcurl_multi_perform = NULL; + qcurl_multi_cleanup = NULL; + qcurl_multi_info_read = NULL; + qcurl_multi_strerror = NULL; +#endif /* USE_CURL_DLOPEN */ +} + +void CL_cURL_Cleanup(void) +{ + if(clc.downloadCURLM) { + if(clc.downloadCURL) { + qcurl_multi_remove_handle(clc.downloadCURLM, + clc.downloadCURL); + qcurl_easy_cleanup(clc.downloadCURL); + } + qcurl_multi_cleanup(clc.downloadCURLM); + clc.downloadCURLM = NULL; + clc.downloadCURL = NULL; + } + else if(clc.downloadCURL) { + qcurl_easy_cleanup(clc.downloadCURL); + clc.downloadCURL = NULL; + } +} + +static int CL_cURL_CallbackProgress( void *dummy, double dltotal, double dlnow, + double ultotal, double ulnow ) +{ + clc.downloadSize = (int)dltotal; + Cvar_SetValue( "cl_downloadSize", clc.downloadSize ); + clc.downloadCount = (int)dlnow; + Cvar_SetValue( "cl_downloadCount", clc.downloadCount ); + return 0; +} + +static int CL_cURL_CallbackWrite(void *buffer, size_t size, size_t nmemb, + void *stream) +{ + FS_Write( buffer, size*nmemb, ((fileHandle_t*)stream)[0] ); + return size*nmemb; +} + +void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ) +{ + clc.cURLUsed = qtrue; + Com_Printf("URL: %s\n", remoteURL); + Com_DPrintf("***** CL_cURL_BeginDownload *****\n" + "Localname: %s\n" + "RemoteURL: %s\n" + "****************************\n", localName, remoteURL); + CL_cURL_Cleanup(); + Q_strncpyz(clc.downloadURL, remoteURL, sizeof(clc.downloadURL)); + Q_strncpyz(clc.downloadName, localName, sizeof(clc.downloadName)); + Com_sprintf(clc.downloadTempName, sizeof(clc.downloadTempName), + "%s.tmp", localName); + + // Set so UI gets access to it + Cvar_Set("cl_downloadName", localName); + Cvar_Set("cl_downloadSize", "0"); + Cvar_Set("cl_downloadCount", "0"); + Cvar_SetValue("cl_downloadTime", cls.realtime); + + clc.downloadBlock = 0; // Starting new file + clc.downloadCount = 0; + + clc.downloadCURL = qcurl_easy_init(); + if(!clc.downloadCURL) { + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_easy_init() " + "failed\n"); + return; + } + clc.download = FS_SV_FOpenFileWrite(clc.downloadTempName); + if(!clc.download) { + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: failed to open " + "%s for writing\n", clc.downloadTempName); + return; + } + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, clc.download); + if(com_developer->integer) + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_VERBOSE, 1); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_URL, clc.downloadURL); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_TRANSFERTEXT, 0); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_REFERER, va("ioQ3://%s", + NET_AdrToString(clc.serverAddress))); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_USERAGENT, va("%s %s", + Q3_VERSION, qcurl_version())); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEFUNCTION, + CL_cURL_CallbackWrite); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_WRITEDATA, &clc.download); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_NOPROGRESS, 0); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSFUNCTION, + CL_cURL_CallbackProgress); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_PROGRESSDATA, NULL); + qcurl_easy_setopt(clc.downloadCURL, CURLOPT_FAILONERROR, 1); + clc.downloadCURLM = qcurl_multi_init(); + if(!clc.downloadCURLM) { + qcurl_easy_cleanup(clc.downloadCURL); + clc.downloadCURL = NULL; + Com_Error(ERR_DROP, "CL_cURL_BeginDownload: qcurl_multi_init() " + "failed\n"); + return; + } + qcurl_multi_add_handle(clc.downloadCURLM, clc.downloadCURL); + + if(!(clc.sv_allowDownload & DLF_NO_DISCONNECT) && + !clc.cURLDisconnected) { + + CL_AddReliableCommand("disconnect"); + CL_WritePacket(); + CL_WritePacket(); + CL_WritePacket(); + clc.cURLDisconnected = qtrue; + } +} + +void CL_cURL_PerformDownload(void) +{ + CURLMcode res; + CURLMsg *msg; + int c; + int i = 0; + + res = qcurl_multi_perform(clc.downloadCURLM, &c); + while(res == CURLM_CALL_MULTI_PERFORM && i < 100) { + res = qcurl_multi_perform(clc.downloadCURLM, &c); + i++; + } + if(res == CURLM_CALL_MULTI_PERFORM) + return; + msg = qcurl_multi_info_read(clc.downloadCURLM, &c); + if(msg == NULL) { + return; + } + FS_FCloseFile(clc.download); + if(msg->msg == CURLMSG_DONE && msg->data.result == CURLE_OK) { + FS_SV_Rename(clc.downloadTempName, clc.downloadName); + clc.downloadRestart = qtrue; + } + else { + long code; + + qcurl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, + &code); + Com_Error(ERR_DROP, "Download Error: %s Code: %d URL: %s", + qcurl_easy_strerror(msg->data.result), + code, clc.downloadURL); + } + *clc.downloadTempName = *clc.downloadName = 0; + Cvar_Set( "cl_downloadName", "" ); + CL_NextDownload(); +} +#endif /* USE_CURL */ diff --git a/src/client/cl_curl.h b/src/client/cl_curl.h new file mode 100644 index 00000000..7b3f8929 --- /dev/null +++ b/src/client/cl_curl.h @@ -0,0 +1,101 @@ +/* +=========================================================================== +Copyright (C) 2006 Tony J. White (tjw@tjw.org) + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#ifndef __QCURL_H__ +#define __QCURL_H__ + +extern cvar_t *cl_cURLLib; + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" + +#ifdef WIN32 +#define DEFAULT_CURL_LIB "libcurl-3.dll" +#elif defined(MACOS_X) +#define DEFAULT_CURL_LIB "libcurl.dylib" +#else +#define DEFAULT_CURL_LIB "libcurl.so.3" +#endif + +#if USE_LOCAL_HEADERS + #include "../libcurl/curl/curl.h" +#else + #include <curl/curl.h> +#endif + + +#if USE_CURL_DLOPEN +extern char* (*qcurl_version)(void); + +extern CURL* (*qcurl_easy_init)(void); +extern CURLcode (*qcurl_easy_setopt)(CURL *curl, CURLoption option, ...); +extern CURLcode (*qcurl_easy_perform)(CURL *curl); +extern void (*qcurl_easy_cleanup)(CURL *curl); +extern CURLcode (*qcurl_easy_getinfo)(CURL *curl, CURLINFO info, ...); +extern void (*qcurl_easy_reset)(CURL *curl); +extern const char *(*qcurl_easy_strerror)(CURLcode); + +extern CURLM* (*qcurl_multi_init)(void); +extern CURLMcode (*qcurl_multi_add_handle)(CURLM *multi_handle, + CURL *curl_handle); +extern CURLMcode (*qcurl_multi_remove_handle)(CURLM *multi_handle, + CURL *curl_handle); +extern CURLMcode (*qcurl_multi_fdset)(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); +extern CURLMcode (*qcurl_multi_perform)(CURLM *multi_handle, + int *running_handles); +extern CURLMcode (*qcurl_multi_cleanup)(CURLM *multi_handle); +extern CURLMsg *(*qcurl_multi_info_read)(CURLM *multi_handle, + int *msgs_in_queue); +extern const char *(*qcurl_multi_strerror)(CURLMcode); +#else +#define qcurl_version curl_version + +#define qcurl_easy_init curl_easy_init +#define qcurl_easy_setopt curl_easy_setopt +#define qcurl_easy_perform curl_easy_perform +#define qcurl_easy_cleanup curl_easy_cleanup +#define qcurl_easy_getinfo curl_easy_getinfo +#define qcurl_easy_duphandle curl_easy_duphandle +#define qcurl_easy_reset curl_easy_reset +#define qcurl_easy_strerror curl_easy_strerror + +#define qcurl_multi_init curl_multi_init +#define qcurl_multi_add_handle curl_multi_add_handle +#define qcurl_multi_remove_handle curl_multi_remove_handle +#define qcurl_multi_fdset curl_multi_fdset +#define qcurl_multi_perform curl_multi_perform +#define qcurl_multi_cleanup curl_multi_cleanup +#define qcurl_multi_info_read curl_multi_info_read +#define qcurl_multi_strerror curl_multi_strerror +#endif + +qboolean CL_cURL_Init( void ); +void CL_cURL_Shutdown( void ); +void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ); +void CL_cURL_PerformDownload( void ); +void CL_cURL_Cleanup( void ); +#endif // __QCURL_H__ diff --git a/src/client/cl_keys.c b/src/client/cl_keys.c index 691a59e2..c65ce916 100644 --- a/src/client/cl_keys.c +++ b/src/client/cl_keys.c @@ -1071,7 +1071,8 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) { if (!down) { return; } - Con_ToggleConsole_f (); + Con_ToggleConsole_f (); + Key_ClearStates (); return; } diff --git a/src/client/cl_main.c b/src/client/cl_main.c index 808553b9..aefbab8c 100644 --- a/src/client/cl_main.c +++ b/src/client/cl_main.c @@ -655,6 +655,9 @@ CL_ShutdownAll */ void CL_ShutdownAll(void) { +#if USE_CURL + CL_cURL_Shutdown(); +#endif // clear sounds S_DisableSounds(); // shutdown CGame @@ -1159,6 +1162,9 @@ void CL_Vid_Restart_f( void ) { CL_CloseAVI( ); } + if(clc.demorecording) + CL_StopRecord_f(); + // don't let them loop during the restart S_StopAllSounds(); // shutdown the UI @@ -1291,6 +1297,23 @@ Called when all downloading has been completed */ void CL_DownloadsComplete( void ) { +#if USE_CURL + // if we downloaded with cURL + if(clc.cURLUsed) { + clc.cURLUsed = qfalse; + CL_cURL_Shutdown(); + if( clc.cURLDisconnected ) { + if(clc.downloadRestart) { + FS_Restart(clc.checksumFeed); + clc.downloadRestart = qfalse; + } + clc.cURLDisconnected = qfalse; + CL_Reconnect_f(); + return; + } + } +#endif + // if we downloaded files we need to restart the file system if (clc.downloadRestart) { clc.downloadRestart = qfalse; @@ -1378,6 +1401,7 @@ A download completed or failed void CL_NextDownload(void) { char *s; char *remoteName, *localName; + qboolean useCURL = qfalse; // We are looking to start a download here if (*clc.downloadList) { @@ -1401,9 +1425,48 @@ void CL_NextDownload(void) { *s++ = 0; else s = localName + strlen(localName); // point at the nul byte - - CL_BeginDownload( localName, remoteName ); - +#if USE_CURL + if(!(cl_allowDownload->integer & DLF_NO_REDIRECT)) { + if(clc.sv_allowDownload & DLF_NO_REDIRECT) { + Com_Printf("WARNING: server does not " + "allow download redirection " + "(sv_allowDownload is %d)\n", + clc.sv_allowDownload); + } + else if(!*clc.sv_dlURL) { + Com_Printf("WARNING: server allows " + "download redirection, but does not " + "have sv_dlURL set\n"); + } + else if(!CL_cURL_Init()) { + Com_Printf("WARNING: could not load " + "cURL library\n"); + } + else { + CL_cURL_BeginDownload(localName, va("%s/%s", + clc.sv_dlURL, remoteName)); + useCURL = qtrue; + } + } + else if(!(clc.sv_allowDownload & DLF_NO_REDIRECT)) { + Com_Printf("WARNING: server allows download " + "redirection, but it disabled by client " + "configuration (cl_allowDownload is %d)\n", + cl_allowDownload->integer); + } +#endif /* USE_CURL */ + if(!useCURL) { + if((cl_allowDownload->integer & DLF_NO_UDP)) { + Com_Error(ERR_DROP, "UDP Downloads are " + "disabled on your client. " + "(cl_allowDownload is %d)", + cl_allowDownload->integer); + return; + } + else { + CL_BeginDownload( localName, remoteName ); + } + } clc.downloadRestart = qtrue; // move over the rest @@ -1426,7 +1489,7 @@ and determine if we need to download them void CL_InitDownloads(void) { char missingfiles[1024]; - if ( !cl_allowDownload->integer ) + if ( !(cl_allowDownload->integer & DLF_ENABLE) ) { // autodownload is disabled on the client // but it's possible that some referenced files on the server are missing @@ -1911,7 +1974,7 @@ void CL_CheckTimeout( void ) { // // check timeout // - if ( ( !cl_paused->integer || !sv_paused->integer ) + if ( ( !CL_CheckPaused() || !sv_paused->integer ) && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) { if (++cl.timeoutcount > 5) { // timeoutcount saves debugger @@ -1924,6 +1987,22 @@ void CL_CheckTimeout( void ) { } } +/* +================== +CL_CheckPaused +Check whether client has been paused. +================== +*/ +qboolean CL_CheckPaused(void) +{ + // if cl_paused->modified is set, the cvar has only been changed in + // this frame. Keep paused in this frame to ensure the server doesn't + // lag behind. + if(cl_paused->integer || cl_paused->modified) + return qtrue; + + return qfalse; +} //============================================================================ @@ -1935,19 +2014,19 @@ CL_CheckUserinfo */ void CL_CheckUserinfo( void ) { // don't add reliable commands when not yet connected - if ( cls.state < CA_CHALLENGING ) { + if(cls.state < CA_CHALLENGING) return; - } + // don't overflow the reliable command buffer when paused - if ( cl_paused->integer ) { + if(CL_CheckPaused()) return; - } + // send a reliable userinfo update if needed - if ( cvar_modifiedFlags & CVAR_USERINFO ) { + if(cvar_modifiedFlags & CVAR_USERINFO) + { cvar_modifiedFlags &= ~CVAR_USERINFO; CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) ); } - } /* @@ -1962,6 +2041,25 @@ void CL_Frame ( int msec ) { return; } +#if USE_CURL + if(clc.downloadCURLM) { + CL_cURL_PerformDownload(); + // we can't process frames normally when in disconnected + // download mode since the ui vm expects cls.state to be + // CA_CONNECTED + if(clc.cURLDisconnected) { + cls.realFrametime = msec; + cls.frametime = msec; + cls.realtime += cls.frametime; + SCR_UpdateScreen(); + S_Update(); + Con_RunConsole(); + cls.framecount++; + return; + } + } +#endif + if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) && !com_sv_running->integer ) { // if disconnected, bring up the menu @@ -2273,6 +2371,12 @@ void CL_Video_f( void ) char filename[ MAX_OSPATH ]; int i, last; + if( !clc.demoplaying ) + { + Com_Printf( "The video command can only be used when playing back demos\n" ); + return; + } + if( Cmd_Argc( ) == 2 ) { // explicit filename @@ -2404,6 +2508,9 @@ void CL_Init( void ) { cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE); +#if USE_CURL + cl_cURLLib = Cvar_Get("cl_cURLLib", DEFAULT_CURL_LIB, CVAR_ARCHIVE); +#endif cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); #ifdef MACOS_X @@ -2718,7 +2825,7 @@ void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING ); if (strlen(info)) { if (info[strlen(info)-1] != '\n') { - strncat(info, "\n", sizeof(info)); + strncat(info, "\n", sizeof(info) - 1); } Com_Printf( "%s: %s", NET_AdrToString( from ), info ); } diff --git a/src/client/cl_parse.c b/src/client/cl_parse.c index 7de07e91..093893a6 100644 --- a/src/client/cl_parse.c +++ b/src/client/cl_parse.c @@ -220,6 +220,10 @@ void CL_ParseSnapshot( msg_t *msg ) { newSnap.serverTime = MSG_ReadLong( msg ); + // if we were just unpaused, we can only *now* really let the + // change come into effect or the client hangs. + cl_paused->modified = 0; + newSnap.messageNum = clc.serverMessageSequence; deltaNum = MSG_ReadByte( msg ); @@ -411,6 +415,25 @@ void CL_SystemInfoChanged( void ) { /* ================== +CL_ParseServerInfo +================== +*/ +static void CL_ParseServerInfo(void) +{ + const char *serverInfo; + + serverInfo = cl.gameState.stringData + + cl.gameState.stringOffsets[ CS_SERVERINFO ]; + + clc.sv_allowDownload = atoi(Info_ValueForKey(serverInfo, + "sv_allowDownload")); + Q_strncpyz(clc.sv_dlURL, + Info_ValueForKey(serverInfo, "sv_dlURL"), + sizeof(clc.sv_dlURL)); +} + +/* +================== CL_ParseGamestate ================== */ @@ -476,11 +499,18 @@ void CL_ParseGamestate( msg_t *msg ) { // read the checksum feed clc.checksumFeed = MSG_ReadLong( msg ); + // parse useful values out of CS_SERVERINFO + CL_ParseServerInfo(); + // parse serverId and other cvars CL_SystemInfoChanged(); + // stop recording now so the demo won't have an unnecessary level load at the end. + if(cl_autoRecordDemo->integer && clc.demorecording) + CL_StopRecord_f(); + // reinitialize the filesystem if the game directory has changed - FS_ConditionalRestart( clc.checksumFeed ); + FS_ConditionalRestart( clc.checksumFeed ); // This used to call CL_StartHunkUsers, but now we enter the download state before loading the // cgame diff --git a/src/client/cl_ui.c b/src/client/cl_ui.c index 327eaacf..caa9179f 100644 --- a/src/client/cl_ui.c +++ b/src/client/cl_ui.c @@ -603,10 +603,10 @@ static void CL_GetGlconfig( glconfig_t *config ) { /* ==================== -GetClipboardData +CL_GetClipboardData ==================== */ -static void GetClipboardData( char *buf, int buflen ) { +static void CL_GetClipboardData( char *buf, int buflen ) { char *cbd; cbd = Sys_GetClipboardData(); @@ -836,7 +836,7 @@ intptr_t CL_UISystemCalls( intptr_t *args ) { return 0; case UI_GETCLIPBOARDDATA: - GetClipboardData( VMA(1), args[2] ); + CL_GetClipboardData( VMA(1), args[2] ); return 0; case UI_GETCLIENTSTATE: diff --git a/src/client/client.h b/src/client/client.h index ae5915db..77cf7dca 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -31,12 +31,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../cgame/cg_public.h" #include "../game/bg_public.h" +#if USE_CURL +#include "cl_curl.h" +#endif /* USE_CURL */ + // tjw: file full of random crap that gets used to create cl_guid #define QKEY_FILE "qkey" #define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits - // snapshots are a view of the server at a given time typedef struct { qboolean valid; // cleared if delta parsing was invalid @@ -187,6 +190,16 @@ typedef struct { fileHandle_t download; char downloadTempName[MAX_OSPATH]; char downloadName[MAX_OSPATH]; +#ifdef USE_CURL + qboolean cURLEnabled; + qboolean cURLUsed; + qboolean cURLDisconnected; + char downloadURL[MAX_OSPATH]; + CURL *downloadCURL; + CURLM *downloadCURLM; +#endif /* USE_CURL */ + int sv_allowDownload; + char sv_dlURL[MAX_CVAR_VALUE_STRING]; int downloadNumber; int downloadBlock; // block we are waiting for int downloadCount; // how many bytes we got @@ -352,10 +365,12 @@ extern cvar_t *cl_aviMotionJpeg; extern cvar_t *cl_activeAction; extern cvar_t *cl_allowDownload; +extern cvar_t *cl_downloadMethod; extern cvar_t *cl_conXOffset; extern cvar_t *cl_inGameVideo; extern cvar_t *cl_lanForcePackets; +extern cvar_t *cl_autoRecordDemo; //================================================= @@ -380,6 +395,7 @@ void CL_ReadDemoMessage( void ); demoState_t CL_DemoState( void ); int CL_DemoPos( void ); void CL_DemoName( char *buffer, int size ); +void CL_StopRecord_f( void ); void CL_InitDownloads(void); void CL_NextDownload(void); @@ -393,6 +409,7 @@ void CL_ShutdownRef( void ); void CL_InitRef( void ); int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ); +qboolean CL_CheckPaused(void); // // cl_input diff --git a/src/client/qal.c b/src/client/qal.c index b206881f..4c04bd41 100644 --- a/src/client/qal.c +++ b/src/client/qal.c @@ -177,8 +177,8 @@ qboolean QAL_Init(const char *libname) #else char fn[1024]; getcwd(fn, sizeof(fn)); - strncat(fn, "/", sizeof(fn)); - strncat(fn, libname, sizeof(fn)); + strncat(fn, "/", sizeof(fn) - strlen(fn) - 1); + strncat(fn, libname, sizeof(fn) - strlen(fn) - 1); if( (OpenALLib = OBJLOAD(fn)) == 0 ) { diff --git a/src/client/snd_codec_ogg.c b/src/client/snd_codec_ogg.c index 72ece944..f13f9a8d 100644 --- a/src/client/snd_codec_ogg.c +++ b/src/client/snd_codec_ogg.c @@ -4,20 +4,20 @@ Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com) Copyright (C) 2005-2006 Joerg Dietrich <dietrich_joerg@gmx.de> -This file is part of Quake III Arena source code. +This file is part of Tremulous. -Quake III Arena source code is free software; you can redistribute it +Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. -Quake III Arena source code is distributed in the hope that it will be +Tremulous is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software +along with Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ diff --git a/src/client/snd_main.c b/src/client/snd_main.c index 8f09570e..eb31f2d7 100644 --- a/src/client/snd_main.c +++ b/src/client/snd_main.c @@ -405,9 +405,9 @@ void S_Init( void ) } S_SoundInfo( ); - Com_Printf( "Sound intialization successful.\n" ); + Com_Printf( "Sound initialization successful.\n" ); } else { - Com_Printf( "Sound intialization failed.\n" ); + Com_Printf( "Sound initialization failed.\n" ); } } diff --git a/src/client/snd_openal.c b/src/client/snd_openal.c index 39a04179..6f5396c4 100644 --- a/src/client/snd_openal.c +++ b/src/client/snd_openal.c @@ -37,8 +37,12 @@ cvar_t *s_alSources; cvar_t *s_alDopplerFactor; cvar_t *s_alDopplerSpeed; cvar_t *s_alMinDistance; +cvar_t *s_alMaxDistance; cvar_t *s_alRolloff; +cvar_t *s_alGraceDistance; cvar_t *s_alDriver; +cvar_t *s_alDevice; +cvar_t *s_alAvailableDevices; cvar_t *s_alMaxSpeakerDistance; /* @@ -134,7 +138,11 @@ static sfxHandle_t S_AL_BufferFindFree( void ) { // Got one if(knownSfx[i].filename[0] == '\0') + { + if(i >= numSfx) + numSfx = i + 1; return i; + } } // Shit... @@ -155,7 +163,7 @@ static sfxHandle_t S_AL_BufferFind(const char *filename) sfxHandle_t sfx = -1; int i; - for(i = 0; i < MAX_SFX; i++) + for(i = 0; i < numSfx; i++) { if(!Q_stricmp(knownSfx[i].filename, filename)) { @@ -230,7 +238,7 @@ static qboolean S_AL_BufferEvict( void ) int i, oldestBuffer = -1; int oldestTime = Sys_Milliseconds( ); - for( i = 0; i < MAX_SFX; i++ ) + for( i = 0; i < numSfx; i++ ) { if( !knownSfx[ i ].filename[ 0 ] ) continue; @@ -404,7 +412,7 @@ void S_AL_BufferShutdown( void ) knownSfx[default_sfx].isLocked = qfalse; // Free all used effects - for(i = 0; i < MAX_SFX; i++) + for(i = 0; i < numSfx; i++) S_AL_BufferUnload(i); // Clear the tables @@ -463,6 +471,9 @@ typedef struct src_s int isLooping; // Is this a looping effect (attached to an entity) int isTracking; // Is this object tracking it's owner + float curGain; // gain employed if source is within maxdistance. + float scaleGain; // Last gain value for this source. 0 if muted. + qboolean local; // Is this local (relative to the cam) } src_t; @@ -512,6 +523,50 @@ static void _S_AL_SanitiseVector( vec3_t v, int line ) /* ================= +S_AL_ScaleGain +Adapt the gain if necessary to get a quicker fadeout when the source is too far away. +================= +*/ + +static void S_AL_ScaleGain(src_t *chksrc, vec3_t origin) +{ + float distance; + + if(chksrc->local) + distance = VectorLength(origin); + else + distance = Distance(origin, lastListenerOrigin); + + // If we exceed a certain distance, scale the gain linearly until the sound + // vanishes into nothingness. + if((distance -= s_alMaxDistance->value) > 0) + { + float scaleFactor; + + if(distance >= s_alGraceDistance->value) + scaleFactor = 0.0f; + else + scaleFactor = 1.0f - distance / s_alGraceDistance->value; + + scaleFactor *= chksrc->curGain; + + if(chksrc->scaleGain != scaleFactor); + { + chksrc->scaleGain = scaleFactor; + // if(scaleFactor > 0.0f) + // Com_Printf("%f\n", scaleFactor); + qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain); + } + } + else if(chksrc->scaleGain != chksrc->curGain) + { + chksrc->scaleGain = chksrc->curGain; + qalSourcef(chksrc->alSource, AL_GAIN, chksrc->scaleGain); + } +} + +/* +================= S_AL_HearingThroughEntity ================= */ @@ -615,41 +670,46 @@ static void S_AL_SrcSetup(srcHandle_t src, sfxHandle_t sfx, alSrcPriority_t prio int entity, int channel, qboolean local) { ALuint buffer; + src_t *curSource; // Mark the SFX as used, and grab the raw AL buffer S_AL_BufferUse(sfx); buffer = S_AL_BufferGet(sfx); // Set up src struct - srcList[src].lastUsedTime = Sys_Milliseconds(); - srcList[src].sfx = sfx; - srcList[src].priority = priority; - srcList[src].entity = entity; - srcList[src].channel = channel; - srcList[src].isActive = qtrue; - srcList[src].isLocked = qfalse; - srcList[src].isLooping = qfalse; - srcList[src].isTracking = qfalse; - srcList[src].local = local; + curSource = &srcList[src]; + + curSource->lastUsedTime = Sys_Milliseconds(); + curSource->sfx = sfx; + curSource->priority = priority; + curSource->entity = entity; + curSource->channel = channel; + curSource->isActive = qtrue; + curSource->isLocked = qfalse; + curSource->isLooping = qfalse; + curSource->isTracking = qfalse; + curSource->curGain = s_alGain->value * s_volume->value; + curSource->scaleGain = curSource->curGain; + curSource->local = local; // Set up OpenAL source - qalSourcei(srcList[src].alSource, AL_BUFFER, buffer); - qalSourcef(srcList[src].alSource, AL_PITCH, 1.0f); - qalSourcef(srcList[src].alSource, AL_GAIN, s_alGain->value * s_volume->value); - qalSourcefv(srcList[src].alSource, AL_POSITION, vec3_origin); - qalSourcefv(srcList[src].alSource, AL_VELOCITY, vec3_origin); - qalSourcei(srcList[src].alSource, AL_LOOPING, AL_FALSE); - qalSourcef(srcList[src].alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); + qalSourcei(curSource->alSource, AL_BUFFER, buffer); + qalSourcef(curSource->alSource, AL_PITCH, 1.0f); + qalSourcef(curSource->alSource, AL_GAIN, curSource->curGain); + qalSourcefv(curSource->alSource, AL_POSITION, vec3_origin); + qalSourcefv(curSource->alSource, AL_VELOCITY, vec3_origin); + qalSourcei(curSource->alSource, AL_LOOPING, AL_FALSE); + qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); if(local) { - qalSourcei(srcList[src].alSource, AL_SOURCE_RELATIVE, AL_TRUE); - qalSourcef(srcList[src].alSource, AL_ROLLOFF_FACTOR, 0.0f); + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f); } else { - qalSourcei(srcList[src].alSource, AL_SOURCE_RELATIVE, AL_FALSE); - qalSourcef(srcList[src].alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); } } @@ -825,6 +885,27 @@ void S_AL_UpdateEntityPosition( int entityNum, const vec3_t origin ) /* ================= +S_AL_CheckInput +Check whether input values from mods are out of range. +Necessary for i.g. Western Quake3 mod which is buggy. +================= +*/ +static qboolean S_AL_CheckInput(int entityNum, sfxHandle_t sfx) +{ + if (entityNum < 0 || entityNum > MAX_GENTITIES) + Com_Error(ERR_DROP, "S_StartSound: bad entitynum %i", entityNum); + + if (sfx < 0 || sfx >= numSfx) + { + Com_Printf(S_COLOR_RED, "ERROR: S_AL_CheckInput: handle %i out of range\n", sfx); + return qtrue; + } + + return qfalse; +} + +/* +================= S_AL_StartLocalSound Play a local (non-spatialized) sound effect @@ -833,6 +914,9 @@ Play a local (non-spatialized) sound effect static void S_AL_StartLocalSound(sfxHandle_t sfx, int channel) { + if(S_AL_CheckInput(0, sfx)) + return; + // Try to grab a source srcHandle_t src = S_AL_SrcAlloc(SRCPRI_LOCAL, -1, channel); if(src == -1) @@ -857,6 +941,9 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx { vec3_t sorigin; + if(S_AL_CheckInput(origin ? 0 : entnum, sfx)) + return; + // Try to grab a source srcHandle_t src = S_AL_SrcAlloc(SRCPRI_ONESHOT, entnum, entchannel); if(src == -1) @@ -865,8 +952,6 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx // Set up the effect if( origin == NULL ) { - srcList[ src ].isTracking = qtrue; - if( S_AL_HearingThroughEntity( entnum ) ) { // Where the entity is the local player, play a local sound @@ -878,6 +963,7 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx S_AL_SrcSetup( src, sfx, SRCPRI_ONESHOT, entnum, entchannel, qfalse ); VectorCopy( entityList[ entnum ].origin, sorigin ); } + srcList[ src ].isTracking = qtrue; } else { @@ -887,6 +973,7 @@ void S_AL_StartSound( vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx S_AL_SanitiseVector( sorigin ); qalSourcefv( srcList[ src ].alSource, AL_POSITION, sorigin ); + S_AL_ScaleGain(&srcList[src], sorigin); // Start it playing qalSourcePlay(srcList[src].alSource); @@ -918,6 +1005,7 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, { int src; sentity_t *sent = &entityList[ entityNum ]; + src_t *curSource; // Do we need to allocate a new source for this entity if( !sent->srcAllocated ) @@ -945,28 +1033,33 @@ static void S_AL_SrcLoop( alSrcPriority_t priority, sfxHandle_t sfx, // If this is not set then the looping sound is removed sent->loopAddedThisFrame = qtrue; + curSource = &srcList[src]; + // UGH // These lines should be called via S_AL_SrcSetup, but we // can't call that yet as it buffers sfxes that may change // with subsequent calls to S_AL_SrcLoop - srcList[ src ].entity = entityNum; - srcList[ src ].isLooping = qtrue; - srcList[ src ].isActive = qtrue; + curSource->entity = entityNum; + curSource->isLooping = qtrue; + curSource->isActive = qtrue; if( S_AL_HearingThroughEntity( entityNum ) ) { - srcList[ src ].local = qtrue; + curSource->local = qtrue; - qalSourcefv( srcList[ src ].alSource, AL_POSITION, vec3_origin ); - qalSourcefv( srcList[ src ].alSource, AL_VELOCITY, vec3_origin ); + qalSourcefv( curSource->alSource, AL_POSITION, vec3_origin ); + qalSourcefv( curSource->alSource, AL_VELOCITY, vec3_origin ); } else { - srcList[ src ].local = qfalse; + curSource->local = qfalse; - qalSourcefv( srcList[ src ].alSource, AL_POSITION, (ALfloat *)sent->origin ); - qalSourcefv( srcList[ src ].alSource, AL_VELOCITY, (ALfloat *)velocity ); + qalSourcefv( curSource->alSource, AL_POSITION, (ALfloat *)sent->origin ); + qalSourcefv( curSource->alSource, AL_VELOCITY, (ALfloat *)velocity ); + } + + S_AL_ScaleGain(curSource, sent->origin); } /* @@ -977,6 +1070,9 @@ S_AL_AddLoopingSound static void S_AL_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { + if(S_AL_CheckInput(entityNum, sfx)) + return; + S_AL_SanitiseVector( (vec_t *)origin ); S_AL_SanitiseVector( (vec_t *)velocity ); S_AL_SrcLoop(SRCPRI_ENTITY, sfx, origin, velocity, entityNum); @@ -990,6 +1086,9 @@ S_AL_AddRealLoopingSound static void S_AL_AddRealLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx ) { + if(S_AL_CheckInput(entityNum, sfx)) + return; + S_AL_SanitiseVector( (vec_t *)origin ); S_AL_SanitiseVector( (vec_t *)velocity ); @@ -1028,63 +1127,65 @@ void S_AL_SrcUpdate( void ) int i; int entityNum; ALint state; + src_t *curSource; for(i = 0; i < srcCount; i++) { entityNum = srcList[i].entity; + curSource = &srcList[i]; - if(srcList[i].isLocked) + if(curSource->isLocked) continue; - if(!srcList[i].isActive) + if(!curSource->isActive) continue; // Update source parameters if((s_alGain->modified)||(s_volume->modified)) - qalSourcef(srcList[i].alSource, AL_GAIN, s_alGain->value * s_volume->value); - if((s_alRolloff->modified)&&(!srcList[i].local)) - qalSourcef(srcList[i].alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); + curSource->curGain = s_alGain->value * s_volume->value; + if((s_alRolloff->modified)&&(!curSource->local)) + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); if(s_alMinDistance->modified) - qalSourcef(srcList[i].alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); + qalSourcef(curSource->alSource, AL_REFERENCE_DISTANCE, s_alMinDistance->value); - if( srcList[ i ].isLooping ) + if(curSource->isLooping) { sentity_t *sent = &entityList[ entityNum ]; // If a looping effect hasn't been touched this frame, kill it - if( sent->loopAddedThisFrame ) + if(sent->loopAddedThisFrame) { // The sound has changed without an intervening removal - if( srcList[ i ].isActive && !sent->startLoopingSound && - srcList[ i ].sfx != sent->loopSfx ) + if(curSource->isActive && !sent->startLoopingSound && + curSource->sfx != sent->loopSfx) { - qalSourceStop( srcList[ i ].alSource ); - qalSourcei( srcList[ i ].alSource, AL_BUFFER, 0 ); + qalSourceStop(curSource->alSource); + qalSourcei(curSource->alSource, AL_BUFFER, 0); sent->startLoopingSound = qtrue; } - // Ths sound hasn't been started yet - if( sent->startLoopingSound ) + // The sound hasn't been started yet + if(sent->startLoopingSound) { - S_AL_SrcSetup( i, sent->loopSfx, sent->loopPriority, - entityNum, -1, srcList[ i ].local ); - srcList[ i ].isLooping = qtrue; - qalSourcei( srcList[ i ].alSource, AL_LOOPING, AL_TRUE ); - qalSourcePlay( srcList[ i ].alSource ); + S_AL_SrcSetup(i, sent->loopSfx, sent->loopPriority, + entityNum, -1, curSource->local); + curSource->isLooping = qtrue; + qalSourcei(curSource->alSource, AL_LOOPING, AL_TRUE); + qalSourcePlay(curSource->alSource); sent->startLoopingSound = qfalse; } // Update locality - if( srcList[ i ].local) + if(curSource->local) { - qalSourcei( srcList[ i ].alSource, AL_SOURCE_RELATIVE, AL_TRUE ); - qalSourcef( srcList[ i ].alSource, AL_ROLLOFF_FACTOR, 0.0f ); + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_TRUE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, 0.0f); } else { - qalSourcei( srcList[ i ].alSource, AL_SOURCE_RELATIVE, AL_FALSE ); - qalSourcef( srcList[ i ].alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value ); + qalSourcei(curSource->alSource, AL_SOURCE_RELATIVE, AL_FALSE); + qalSourcef(curSource->alSource, AL_ROLLOFF_FACTOR, s_alRolloff->value); } } else @@ -1093,20 +1194,23 @@ void S_AL_SrcUpdate( void ) continue; } - // Query relativity of source, don't move if it's true - qalGetSourcei( srcList[ i ].alSource, AL_SOURCE_RELATIVE, &state ); - - // See if it needs to be moved - if( srcList[ i ].isTracking && !state ) - qalSourcefv(srcList[i].alSource, AL_POSITION, entityList[entityNum].origin); - // Check if it's done, and flag it - qalGetSourcei(srcList[i].alSource, AL_SOURCE_STATE, &state); + qalGetSourcei(curSource->alSource, AL_SOURCE_STATE, &state); if(state == AL_STOPPED) { S_AL_SrcKill(i); continue; } + + // Query relativity of source, don't move if it's true + qalGetSourcei(curSource->alSource, AL_SOURCE_RELATIVE, &state); + + // See if it needs to be moved + if(curSource->isTracking && !state) + { + qalSourcefv(curSource->alSource, AL_POSITION, entityList[entityNum].origin); + S_AL_ScaleGain(curSource, entityList[entityNum].origin); + } } } @@ -1290,6 +1394,7 @@ static ALuint musicSource; static ALuint musicBuffers[NUM_MUSIC_BUFFERS]; static snd_stream_t *mus_stream; +static snd_stream_t *intro_stream; static char s_backgroundLoop[MAX_QPATH]; static byte decode_buffer[MUSIC_BUFFER_SIZE]; @@ -1333,6 +1438,26 @@ static void S_AL_MusicSourceFree( void ) /* ================= +S_AL_CloseMusicFiles +================= +*/ +static void S_AL_CloseMusicFiles(void) +{ + if(intro_stream) + { + S_CodecCloseStream(intro_stream); + intro_stream = NULL; + } + + if(mus_stream) + { + S_CodecCloseStream(mus_stream); + mus_stream = NULL; + } +} + +/* +================= S_AL_StopBackgroundTrack ================= */ @@ -1355,9 +1480,7 @@ void S_AL_StopBackgroundTrack( void ) S_AL_MusicSourceFree(); // Unload the stream - if(mus_stream) - S_CodecCloseStream(mus_stream); - mus_stream = NULL; + S_AL_CloseMusicFiles(); musicPlaying = qfalse; } @@ -1373,27 +1496,42 @@ void S_AL_MusicProcess(ALuint b) ALenum error; int l; ALuint format; + snd_stream_t *curstream; - if(!mus_stream) + if(intro_stream) + curstream = intro_stream; + else + curstream = mus_stream; + + if(!curstream) return; - l = S_CodecReadStream(mus_stream, MUSIC_BUFFER_SIZE, decode_buffer); + l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer); // Run out data to read, start at the beginning again if(l == 0) { - S_CodecCloseStream(mus_stream); - mus_stream = S_CodecOpenStream(s_backgroundLoop); - if(!mus_stream) + S_CodecCloseStream(curstream); + + // the intro stream just finished playing so we don't need to reopen + // the music stream. + if(intro_stream) + intro_stream = NULL; + else + mus_stream = S_CodecOpenStream(s_backgroundLoop); + + curstream = mus_stream; + + if(!curstream) { S_AL_StopBackgroundTrack(); return; } - l = S_CodecReadStream(mus_stream, MUSIC_BUFFER_SIZE, decode_buffer); + l = S_CodecReadStream(curstream, MUSIC_BUFFER_SIZE, decode_buffer); } - format = S_AL_Format(mus_stream->info.width, mus_stream->info.channels); + format = S_AL_Format(curstream->info.width, curstream->info.channels); if( l == 0 ) { @@ -1403,7 +1541,7 @@ void S_AL_MusicProcess(ALuint b) qalBufferData( b, AL_FORMAT_MONO16, (void *)dummyData, 2, 22050 ); } else - qalBufferData(b, format, decode_buffer, l, mus_stream->info.rate); + qalBufferData(b, format, decode_buffer, l, curstream->info.rate); if( ( error = qalGetError( ) ) != AL_NO_ERROR ) { @@ -1423,27 +1561,12 @@ static void S_AL_StartBackgroundTrack( const char *intro, const char *loop ) { int i; + qboolean issame; // Stop any existing music that might be playing S_AL_StopBackgroundTrack(); - if ( !intro || !intro[0] ) { - intro = loop; - } - if ( !loop || !loop[0] ) { - loop = intro; - } - - if((!intro || !intro[0]) && (!intro || !intro[0])) - return; - - // Copy the loop over - strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); - - // Open the intro - mus_stream = S_CodecOpenStream(intro); - - if(!mus_stream) + if((!intro || !*intro) && (!loop || !*loop)) return; // Allocate a musicSource @@ -1451,9 +1574,32 @@ void S_AL_StartBackgroundTrack( const char *intro, const char *loop ) if(musicSourceHandle == -1) return; + if (!loop || !*loop) + { + loop = intro; + issame = qtrue; + } + else if(intro && *intro && !strcmp(intro, loop)) + issame = qtrue; + else + issame = qfalse; + + // Copy the loop over + strncpy( s_backgroundLoop, loop, sizeof( s_backgroundLoop ) ); + + if(!issame) + { + // Open the intro and don't mind whether it succeeds. + // The important part is the loop. + intro_stream = S_CodecOpenStream(intro); + } + else + intro_stream = NULL; + mus_stream = S_CodecOpenStream(s_backgroundLoop); if(!mus_stream) { + S_AL_CloseMusicFiles(); S_AL_MusicSourceFree(); return; } @@ -1526,6 +1672,7 @@ static ALCcontext *alContext; #ifdef _WIN32 #define ALDRIVER_DEFAULT "OpenAL32.dll" +#define ALDEVICE_DEFAULT "Generic Software" #elif defined(MACOS_X) #define ALDRIVER_DEFAULT "/System/Library/Frameworks/OpenAL.framework/OpenAL" #else @@ -1674,7 +1821,11 @@ void S_AL_SoundInfo( void ) Com_Printf( " Version: %s\n", qalGetString( AL_VERSION ) ); Com_Printf( " Renderer: %s\n", qalGetString( AL_RENDERER ) ); Com_Printf( " Extensions: %s\n", qalGetString( AL_EXTENSIONS ) ); - + if(qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT")) + { + Com_Printf(" Device: %s\n", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); + Com_Printf("Available Devices:\n%s", s_alAvailableDevices->string); + } } /* @@ -1714,6 +1865,9 @@ S_AL_Init qboolean S_AL_Init( soundInterface_t *si ) { #if USE_OPENAL + + qboolean enumsupport, founddev = qfalse; + if( !si ) { return qfalse; } @@ -1725,7 +1879,9 @@ qboolean S_AL_Init( soundInterface_t *si ) s_alDopplerFactor = Cvar_Get( "s_alDopplerFactor", "1.0", CVAR_ARCHIVE ); s_alDopplerSpeed = Cvar_Get( "s_alDopplerSpeed", "2200", CVAR_ARCHIVE ); s_alMinDistance = Cvar_Get( "s_alMinDistance", "120", CVAR_CHEAT ); - s_alRolloff = Cvar_Get( "s_alRolloff", "0.8", CVAR_CHEAT ); + s_alMaxDistance = Cvar_Get("s_alMaxDistance", "1024", CVAR_CHEAT); + s_alRolloff = Cvar_Get( "s_alRolloff", "2", CVAR_CHEAT); + s_alGraceDistance = Cvar_Get("s_alGraceDistance", "512", CVAR_CHEAT); s_alMaxSpeakerDistance = Cvar_Get( "s_alMaxSpeakerDistance", "1024", CVAR_ARCHIVE ); s_alDriver = Cvar_Get( "s_alDriver", ALDRIVER_DEFAULT, CVAR_ARCHIVE ); @@ -1737,8 +1893,56 @@ qboolean S_AL_Init( soundInterface_t *si ) return qfalse; } - // Open default device - alDevice = qalcOpenDevice( NULL ); + // Device enumeration support (extension is implemented reasonably only on Windows right now). + if((enumsupport = qalcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))) + { + char devicenames[1024] = ""; + const char *devicelist; + const char *defaultdevice; + int curlen; + + // get all available devices + the default device name. + devicelist = qalcGetString(NULL, ALC_DEVICE_SPECIFIER); + defaultdevice = qalcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); + +#ifdef _WIN32 + // check whether the default device is generic hardware. If it is, change to + // Generic Software as that one works more reliably with various sound systems. + // If it's not, use OpenAL's default selection as we don't want to ignore + // native hardware acceleration. + if(!strcmp(defaultdevice, "Generic Hardware")) + s_alDevice = Cvar_Get("s_alDevice", ALDEVICE_DEFAULT, CVAR_ARCHIVE | CVAR_LATCH); + else +#endif + s_alDevice = Cvar_Get("s_alDevice", defaultdevice, CVAR_ARCHIVE | CVAR_LATCH); + + // dump a list of available devices to a cvar for the user to see. + while((curlen = strlen(devicelist))) + { + Q_strcat(devicenames, sizeof(devicenames), devicelist); + Q_strcat(devicenames, sizeof(devicenames), "\n"); + + // check whether the device we want to load is available at all. + if(!strcmp(s_alDevice->string, devicelist)) + founddev = qtrue; + + devicelist += curlen + 1; + } + + s_alAvailableDevices = Cvar_Get("s_alAvailableDevices", devicenames, CVAR_ROM | CVAR_NORESTART); + + if(!founddev) + { + Cvar_ForceReset("s_alDevice"); + founddev = 1; + } + } + + if(founddev) + alDevice = qalcOpenDevice(s_alDevice->string); + else + alDevice = qalcOpenDevice(NULL); + if( !alDevice ) { QAL_Shutdown( ); @@ -1746,6 +1950,9 @@ qboolean S_AL_Init( soundInterface_t *si ) return qfalse; } + if(enumsupport) + Cvar_Set("s_alDevice", qalcGetString(alDevice, ALC_DEVICE_SPECIFIER)); + // Create OpenAL context alContext = qalcCreateContext( alDevice, NULL ); if( !alContext ) @@ -1762,6 +1969,7 @@ qboolean S_AL_Init( soundInterface_t *si ) S_AL_SrcInit( ); // Set up OpenAL parameters (doppler, etc) + qalDistanceModel(AL_INVERSE_DISTANCE_CLAMPED); qalDopplerFactor( s_alDopplerFactor->value ); qalDopplerVelocity( s_alDopplerSpeed->value ); |