Commit mod list patch #1415:

1. Adds a specific autoload folder, `mods/autoload/`. This is essential for a good mod community.
 2. Searches the `mods/` folder for global mods, as well as `mods/global/`. This is the first step in merging the three mod folders together, but I'm not planning on merging them completely in 2.3.
 3. Constructs a list of loaded mods, and displays the list in the VersionString (including the list of games in the lobby).
 4. Grays out games with incompatible mods in the NetPlay game list, and gives a warning in the tooltip: "Your loaded mods are incompatible with this game. (Check mods/autoload/?)"
 5. Gives a warning when a game with an incompatible mod is clicked: "You have an incompatible mod."
 6. Displays a list of mods above the Warzone logo.
 7. Lists mods in hosting screen, and warns hosts that all players need the same mods loaded.

Closes ticket:1415.

git-svn-id: https://warzone2100.svn.sourceforge.net/svnroot/warzone2100/trunk@9291 4a71c877-e1ca-e34f-864e-861f7616d084
master
Guangcong Luo 2010-01-17 20:12:03 +00:00 committed by Git SVN Gateway
parent 809098735d
commit 2025407222
6 changed files with 171 additions and 18 deletions

View File

@ -27,6 +27,7 @@
#include "lib/framework/string_ext.h"
#include "lib/gamelib/gtime.h"
#include "src/component.h" // FIXME: we need to handle this better
#include "src/modding.h" // FIXME: we need to handle this better
#include <time.h> // for stats
#include <SDL_timer.h>
#include <SDL_thread.h>
@ -267,8 +268,8 @@ static bool playerPasswordFlag[MAX_PLAYERS] = {false}; // we kick on false
************************************************************************************
**/
char VersionString[VersionStringSize] = "trunk, netcode 3.32";
static int NETCODE_VERSION_MAJOR = 3;
static int NETCODE_VERSION_MINOR = 32;
static int NETCODE_VERSION_MAJOR = 2;
static int NETCODE_VERSION_MINOR = 33;
static int NUMBER_OF_MODS = 0; // unused for now
static int NETCODE_HASH = 0; // unused for now
@ -3438,8 +3439,13 @@ BOOL NEThostGame(const char* SessionName, const char* PlayerName,
gamestruct.desc.dwUserFlags[3] = four;
memset(gamestruct.secondaryHosts, 0, sizeof(gamestruct.secondaryHosts));
sstrcpy(gamestruct.extra, "Extra"); // extra string (future use)
sstrcpy(gamestruct.versionstring, VersionString); // version (string)
sstrcpy(gamestruct.modlist, "Mod list"); // List of mods
sstrcpy(gamestruct.versionstring, VersionString); // version (string)
if (*getModList())
{
sstrcat(gamestruct.versionstring, _(", mods: ")); // version (string)
sstrcat(gamestruct.versionstring, getModList()); // version (string)
}
sstrcpy(gamestruct.modlist, getModList()); // List of mods
gamestruct.GAMESTRUCT_VERSION = 3; // version of this structure
gamestruct.game_version_major = NETCODE_VERSION_MAJOR; // Netcode Major version
gamestruct.game_version_minor = NETCODE_VERSION_MINOR; // NetCode Minor version
@ -3719,9 +3725,10 @@ connect_succesfull:
addressToText(cur->ai_addr, NetPlay.games[gameNumber].desc.host, sizeof(NetPlay.games[gameNumber].desc.host));
}
freeaddrinfo(hosts);
if (NetPlay.games[gameNumber].desc.dwCurrentPlayers >= NetPlay.games[gameNumber].desc.dwMaxPlayers)
if (NetPlay.games[gameNumber].desc.dwCurrentPlayers >= NetPlay.games[gameNumber].desc.dwMaxPlayers ||
strcmp(NetPlay.games[gameNumber].modlist, getModList()) != 0)
{
// Shouldn't join; game is full
// Shouldn't join; game is full or we have an incompatible mod
delSocket(socket_set, tcp_socket);
socketClose(tcp_socket);
free(socket_set);

View File

@ -68,6 +68,7 @@
#include "wrappers.h"
#include "version.h"
#include "configuration.h"
#include "modding.h"
// ////////////////////////////////////////////////////////////////////////////
// Global variables
@ -535,6 +536,8 @@ BOOL runMultiPlayerMenu(void)
switch(id)
{
case FRONTEND_HOST:
// don't pretend we are running a network game. Really do it!
NetPlay.bComms = true; // use network = true
NETdiscoverUPnPDevices();
ingame.bHostSetup = true;
bMultiPlayer = true;
@ -1675,8 +1678,9 @@ void addSideText(UDWORD id, UDWORD PosX, UDWORD PosY, const char *txt)
static void displayTitleBitmap(WZ_DECL_UNUSED WIDGET *psWidget, WZ_DECL_UNUSED UDWORD xOffset, WZ_DECL_UNUSED UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_GREY);
iV_DrawTextRotated(version_getFormattedVersionString(), pie_GetVideoBufferWidth() - 9, pie_GetVideoBufferHeight() - 14, 270.f);
iV_SetTextColour(WZCOL_TEXT_BRIGHT);
iV_DrawTextRotated(version_getFormattedVersionString(), pie_GetVideoBufferWidth() - 10, pie_GetVideoBufferHeight() - 15, 270.f);
}
@ -1685,6 +1689,19 @@ static void displayTitleBitmap(WZ_DECL_UNUSED WIDGET *psWidget, WZ_DECL_UNUSED U
void displayLogo(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, WZ_DECL_UNUSED PIELIGHT *pColours)
{
iV_DrawImage(FrontImages,IMAGE_FE_LOGO,xOffset+psWidget->x,yOffset+psWidget->y);
if (*getModList())
{
// show the mod list
char modListText[WIDG_MAXSTR] = "";
sstrcat(modListText, _("Active mods: "));
sstrcat(modListText, getModList());
iV_SetFont(font_regular);
iV_SetTextColour(WZCOL_GREY);
iV_DrawText(modListText, xOffset+psWidget->x-9, yOffset+psWidget->y-19);
iV_SetTextColour(WZCOL_TEXT_BRIGHT);
iV_DrawText(modListText, xOffset+psWidget->x-10, yOffset+psWidget->y-20);
}
}

View File

@ -250,6 +250,7 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
{
case mod_clean:
debug(LOG_WZ, "Cleaning up");
clearLoadedMods();
while( curSearchPath )
{
@ -291,6 +292,7 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
break;
case mod_campaign:
debug(LOG_WZ, "*** Switching to campaign mods ***");
clearLoadedMods();
while( curSearchPath )
{
@ -300,9 +302,11 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
// Add global and campaign mods
PHYSFS_addToSearchPath( curSearchPath->path, PHYSFS_APPEND );
addSubdirs( curSearchPath->path, "mods/music", PHYSFS_APPEND, NULL );
addSubdirs( curSearchPath->path, "mods/global", PHYSFS_APPEND, global_mods );
addSubdirs( curSearchPath->path, "mods/campaign", PHYSFS_APPEND, campaign_mods );
addSubdirs( curSearchPath->path, "mods/music", PHYSFS_APPEND, NULL, false );
addSubdirs( curSearchPath->path, "mods/global", PHYSFS_APPEND, global_mods, true );
addSubdirs( curSearchPath->path, "mods", PHYSFS_APPEND, global_mods, true );
addSubdirs( curSearchPath->path, "mods/autoload", PHYSFS_APPEND, NULL, true );
addSubdirs( curSearchPath->path, "mods/campaign", PHYSFS_APPEND, campaign_mods, true );
if (!PHYSFS_removeFromSearchPath( curSearchPath->path ))
{
info("* Failed to remove path %s again", curSearchPath->path);
@ -323,12 +327,13 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
sstrcpy(tmpstr, curSearchPath->path);
sstrcat(tmpstr, "sequences.wz");
PHYSFS_addToSearchPath( tmpstr, PHYSFS_APPEND );
curSearchPath = curSearchPath->higherPriority;
}
break;
case mod_multiplay:
debug(LOG_WZ, "*** Switching to multiplay mods ***");
clearLoadedMods();
while( curSearchPath )
{
@ -337,10 +342,12 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
#endif // DEBUG
// Add maps and global and multiplay mods
PHYSFS_addToSearchPath( curSearchPath->path, PHYSFS_APPEND );
addSubdirs( curSearchPath->path, "maps", PHYSFS_APPEND, NULL );
addSubdirs( curSearchPath->path, "mods/music", PHYSFS_APPEND, NULL );
addSubdirs( curSearchPath->path, "mods/global", PHYSFS_APPEND, global_mods );
addSubdirs( curSearchPath->path, "mods/multiplay", PHYSFS_APPEND, multiplay_mods );
addSubdirs( curSearchPath->path, "maps", PHYSFS_APPEND, NULL, false );
addSubdirs( curSearchPath->path, "mods/music", PHYSFS_APPEND, NULL, false );
addSubdirs( curSearchPath->path, "mods/global", PHYSFS_APPEND, global_mods, true );
addSubdirs( curSearchPath->path, "mods", PHYSFS_APPEND, global_mods, true );
addSubdirs( curSearchPath->path, "mods/autoload", PHYSFS_APPEND, NULL, true );
addSubdirs( curSearchPath->path, "mods/multiplay", PHYSFS_APPEND, multiplay_mods, true );
PHYSFS_removeFromSearchPath( curSearchPath->path );
// Add multiplay patches

View File

@ -108,6 +108,10 @@ char * global_mods[MAX_MODS] = { NULL };
char * campaign_mods[MAX_MODS] = { NULL };
char * multiplay_mods[MAX_MODS] = { NULL };
char * loaded_mods[MAX_MODS] = { NULL };
char * mod_list = NULL;
int num_loaded_mods = 0;
// Warzone 2100 . Pumpkin Studios
@ -155,7 +159,7 @@ static BOOL inList( char * list[], const char * item )
* \param appendToPath Whether to append or prepend
* \param checkList List of directories to check. NULL means any.
*/
void addSubdirs( const char * basedir, const char * subdir, const BOOL appendToPath, char * checkList[] )
void addSubdirs( const char * basedir, const char * subdir, const bool appendToPath, char * checkList[], bool addToModList )
{
char tmpstr[PATH_MAX];
char ** subdirlist = PHYSFS_enumerateFiles( subdir );
@ -171,6 +175,10 @@ void addSubdirs( const char * basedir, const char * subdir, const BOOL appendToP
#ifdef DEBUG
debug( LOG_NEVER, "addSubdirs: Adding [%s] to search path", tmpstr );
#endif // DEBUG
if (addToModList)
{
addLoadedMod(*i);
}
PHYSFS_addToSearchPath( tmpstr, appendToPath );
}
i++;
@ -213,6 +221,92 @@ void printSearchPath( void )
PHYSFS_freeList( searchPath );
}
void addLoadedMod(const char * modname)
{
char * mod = strdup(modname);
int i, modlen;
if (num_loaded_mods >= MAX_MODS)
{
// mod list full
return;
}
modlen = strlen(mod);
if (modlen >= 3 && strcmp(&mod[modlen-3], ".wz")==0)
{
// remove ".wz" from end
mod[modlen-3] = 0;
modlen -= 3;
}
if (modlen >= 4 && strcmp(&mod[modlen-4], ".cam")==0)
{
// remove ".cam.wz" from end
mod[modlen-4] = 0;
modlen -= 4;
}
else if (modlen >= 4 && strcmp(&mod[modlen-4], ".mod")==0)
{
// remove ".mod.wz" from end
mod[modlen-4] = 0;
modlen -= 4;
}
else if (modlen >= 5 && strcmp(&mod[modlen-5], ".gmod")==0)
{
// remove ".gmod.wz" from end
mod[modlen-5] = 0;
modlen -= 5;
}
// Yes, this is an online insertion sort.
// I swear, for the numbers of mods this is going to be dealing with
// (i.e. 0 to 2), it really is faster than, say, Quicksort.
for (i=0; i<num_loaded_mods && strcmp(loaded_mods[i], mod)>0; i++);
if (i < num_loaded_mods)
{
if (strcmp(loaded_mods[i], mod) == 0)
{
// mod already in list
free(mod);
return;
}
memmove(&loaded_mods[i+1], &loaded_mods[i], (num_loaded_mods-i)*sizeof(char*));
}
loaded_mods[i] = mod;
num_loaded_mods++;
}
void clearLoadedMods(void)
{
int i;
for (i=0; i<num_loaded_mods; i++)
{
free(loaded_mods[i]);
}
num_loaded_mods = 0;
if (mod_list)
{
free(mod_list);
mod_list = NULL;
}
}
char * getModList(void)
{
int i;
if (mod_list)
{
// mod list already constructed
return mod_list;
}
mod_list = malloc(modlist_string_size);
mod_list[0] = 0; //initialize
for (i=0; i<num_loaded_mods; i++)
{
if (i != 0)
{
strlcat(mod_list, ", ", modlist_string_size);
}
strlcat(mod_list, loaded_mods[i], modlist_string_size);
}
return mod_list;
}
/*!
* Retrieves the current working directory and copies it into the provided output buffer

View File

@ -21,8 +21,12 @@
#ifndef __INCLUDED_SRC_MODDING_H__
#define __INCLUDED_SRC_MODDING_H__
void addSubdirs( const char * basedir, const char * subdir, const BOOL appendToPath, char * checkList[] );
void addSubdirs( const char * basedir, const char * subdir, const bool appendToPath, char * checkList[], bool addToModList );
void removeSubdirs( const char * basedir, const char * subdir, char * checkList[] );
void printSearchPath( void );
void addLoadedMod(const char * modname);
void clearLoadedMods(void);
char * getModList(void);
#endif // __INCLUDED_SRC_MODDING_H__

View File

@ -79,6 +79,7 @@
#include "keymap.h"
#include "game.h"
#include "warzoneconfig.h"
#include "modding.h"
#include "multiplay.h"
#include "multiint.h"
@ -747,6 +748,7 @@ static void addGames(void)
UDWORD i,gcount=0;
W_BUTINIT sButInit;
static const char *wrongVersionTip = "Your version of Warzone is incompatible with this game.";
static const char *badModTip = "Your loaded mods are incompatible with this game. (Check mods/autoload/?)";
//count games to see if need two columns.
for(i=0;i<MaxGames;i++) // draw games
@ -810,6 +812,10 @@ static void addGames(void)
{
sButInit.pTip = wrongVersionTip;
}
else if (strcmp(NetPlay.games[i].modlist,getModList()) != 0)
{
sButInit.pTip = badModTip;
}
else
{
sButInit.pTip = NetPlay.games[i].name;
@ -843,7 +849,7 @@ static void addGames(void)
txt = _("Wrong Game Version!");
break;
case ERROR_WRONGDATA:
txt = _("Wrong data/mod detected by Host.");
txt = _("You have an incompatible mod.");
break;
// AFAIK, the only way this can really happy is if the Host's file is named wrong, or a client side error.
case ERROR_UNKNOWNFILEISSUE:
@ -965,6 +971,11 @@ void runGameFind(void )
setLobbyError(ERROR_FULL);
addGames();
}
else if (strcmp(NetPlay.games[gameNumber].modlist, getModList()) != 0)
{
setLobbyError(ERROR_WRONGDATA);
addGames();
}
else
{
setLobbyError(ERROR_CONNECTION);
@ -2203,6 +2214,18 @@ static void addChatBox(void)
widgAddEditBox(psWScreen, &sEdInit);
if (*getModList())
{
char modListMessage[WIDG_MAXSTR] = "";
sstrcat(modListMessage, _("Active mods: "));
sstrcat(modListMessage, getModList());
addConsoleMessage(modListMessage,DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
if (NetPlay.bComms)
{
addConsoleMessage(_("All players need to have the same mods to join your game."),DEFAULT_JUSTIFY, SYSTEM_MESSAGE);
}
}
return;
}
@ -3364,6 +3387,7 @@ void displayRemoteGame(WIDGET *psWidget, UDWORD xOffset, UDWORD yOffset, PIELIGH
// get game info.
// TODO: Check whether this code is used at all in skirmish games, if not, remove it.
if ((NetPlay.games[i].desc.dwFlags & SESSION_JOINDISABLED)
|| strcmp(NetPlay.games[i].modlist,getModList()) != 0
|| (bMultiPlayer
&& !NetPlay.bComms
&& NETgetGameFlagsUnjoined(gameNumber,1) == SKIRMISH // the LAST bug...