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-861f7616d084master
parent
809098735d
commit
2025407222
|
@ -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
|
||||
|
||||
|
@ -3439,7 +3440,12 @@ BOOL NEThostGame(const char* SessionName, const char* PlayerName,
|
|||
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
|
||||
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
21
src/init.c
21
src/init.c
|
@ -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);
|
||||
|
@ -329,6 +333,7 @@ BOOL rebuildSearchPath( searchPathMode mode, BOOL force )
|
|||
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
|
||||
|
|
96
src/main.c
96
src/main.c
|
@ -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
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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...
|
||||
|
|
Loading…
Reference in New Issue