Merge branch 'next' into elevator-cleanup

# Conflicts:
#	src/p_saveg.c
This commit is contained in:
MascaraSnake 2020-05-01 11:42:05 +02:00
commit 790affd13b
12 changed files with 528 additions and 372 deletions

View File

@ -85,6 +85,10 @@ tic_t jointimeout = (10*TICRATE);
static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame?
static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout?
// Incremented by cv_joindelay when a client joins, decremented each tic.
// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
static tic_t joindelay = 0;
UINT16 pingmeasurecount = 1;
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
@ -3077,6 +3081,8 @@ consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0,
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {32, "MAX"}, {0, NULL}};
consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}};
consvar_t cv_joindelay = {"joindelay", "10", CV_SAVE, joindelay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}};
consvar_t cv_rejointimeout = {"rejointimeout", "Off", CV_SAVE|CV_FLOAT, rejointimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -3164,6 +3170,8 @@ void SV_ResetServer(void)
neededtic = maketic;
tictoclear = maketic;
joindelay = 0;
for (i = 0; i < MAXNETNODES; i++)
ResetNode(i);
@ -3613,6 +3621,9 @@ static void HandleConnect(SINT8 node)
SV_SendRefuse(node, M_GetText("No players from\nthis node."));
else if (luafiletransfers)
SV_SendRefuse(node, M_GetText("The server is broadcasting a file\nrequested by a Lua script.\nPlease wait a bit and then\ntry rejoining."));
else if (netgame && joindelay > 2 * (tic_t)cv_joindelay.value * TICRATE)
SV_SendRefuse(node, va(M_GetText("Too many people are connecting.\nPlease wait %d seconds and then\ntry rejoining."),
(joindelay - 2 * cv_joindelay.value * TICRATE) / TICRATE));
else
{
#ifndef NONET
@ -3670,6 +3681,7 @@ static void HandleConnect(SINT8 node)
DEBFILE("send savegame\n");
}
SV_AddWaitingPlayers(names[0], names[1]);
joindelay += cv_joindelay.value * TICRATE;
player_joining = true;
}
#else
@ -5038,12 +5050,21 @@ void NetUpdate(void)
hu_resynching = true;
}
}
Net_AckTicker();
// Handle timeouts to prevent definitive freezes from happenning
if (server)
{
for (i = 1; i < MAXNETNODES; i++)
if (nodeingame[i] && freezetimeout[i] < I_GetTime())
Net_ConnectionTimeout(i);
// In case the cvar value was lowered
if (joindelay)
joindelay = min(joindelay - 1, 3 * cv_joindelay.value * TICRATE);
}
nowtime /= NEWTICRATERATIO;
if (nowtime > resptime)
{
@ -5051,6 +5072,7 @@ void NetUpdate(void)
M_Ticker();
CON_Ticker();
}
SV_FileSendTicker();
}

View File

@ -515,7 +515,7 @@ extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
extern tic_t servermaxping;
extern consvar_t cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_rejointimeout;
extern consvar_t cv_allownewplayer, cv_joinnextround, cv_maxplayers, cv_joindelay, cv_rejointimeout;
extern consvar_t cv_resynchattempts, cv_blamecfail;
extern consvar_t cv_maxsend, cv_noticedownload, cv_downloadspeed;

View File

@ -573,6 +573,7 @@ void D_RegisterServerCommands(void)
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_joindelay);
CV_RegisterVar(&cv_rejointimeout);
CV_RegisterVar(&cv_resynchattempts);
CV_RegisterVar(&cv_maxsend);

View File

@ -412,9 +412,9 @@ static int libd_cachePatch(lua_State *L)
HUDONLY
luapat = patchinfohead;
lumpnum = W_CheckNumForName(luaL_checkstring(L, 1));
lumpnum = W_CheckNumForLongName(luaL_checkstring(L, 1));
if (lumpnum == LUMPERROR)
lumpnum = W_GetNumForName("MISSING");
lumpnum = W_GetNumForLongName("MISSING");
for (i = 0; i < numluapatches; i++)
{
@ -454,7 +454,7 @@ static int libd_cachePatch(lua_State *L)
numluapatches++;
#else
HUDONLY
LUA_PushUserdata(L, W_CachePatchName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH);
LUA_PushUserdata(L, W_CachePatchLongName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH);
#endif
return 1;
}

View File

@ -1010,16 +1010,8 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
if (!rover)
WRITEUINT8(save_p, ARCH_NULL);
else {
ffloor_t *r2;
UINT16 i = 0;
// search for id
for (r2 = rover->target->ffloors; r2; r2 = r2->next)
{
if (r2 == rover)
break;
i++;
}
if (!r2)
UINT16 i = P_GetFFloorID(rover);
if (i == UINT16_MAX) // invalid ID
WRITEUINT8(save_p, ARCH_NULL);
else
{

View File

@ -1583,7 +1583,7 @@ static menuitem_t OP_ServerOptionsMenu[] =
{IT_HEADER, NULL, "General", NULL, 0},
#ifndef NONET
{IT_STRING | IT_CVAR | IT_CV_STRING,
NULL, "Server name", &cv_servername, 7},
NULL, "Server name", &cv_servername, 7},
{IT_STRING | IT_CVAR, NULL, "Max Players", &cv_maxplayers, 21},
{IT_STRING | IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 26},
{IT_STRING | IT_CVAR, NULL, "Allow players to join", &cv_allownewplayer, 31},
@ -1628,8 +1628,9 @@ static menuitem_t OP_ServerOptionsMenu[] =
#ifndef NONET
{IT_HEADER, NULL, "Advanced", NULL, 225},
{IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231},
{IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 245},
{IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231},
{IT_STRING | IT_CVAR, NULL, "Join delay", &cv_joindelay, 246},
{IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 251},
#endif
};
@ -10822,7 +10823,8 @@ static void M_ServerOptions(INT32 choice)
OP_ServerOptionsMenu[ 3].status = IT_GRAYEDOUT; // Allow add-on downloading
OP_ServerOptionsMenu[ 4].status = IT_GRAYEDOUT; // Allow players to join
OP_ServerOptionsMenu[35].status = IT_GRAYEDOUT; // Master server
OP_ServerOptionsMenu[36].status = IT_GRAYEDOUT; // Attempts to resynchronise
OP_ServerOptionsMenu[36].status = IT_GRAYEDOUT; // Minimum delay between joins
OP_ServerOptionsMenu[37].status = IT_GRAYEDOUT; // Attempts to resynchronise
}
else
{
@ -10834,6 +10836,7 @@ static void M_ServerOptions(INT32 choice)
? IT_GRAYEDOUT
: (IT_STRING | IT_CVAR | IT_CV_STRING));
OP_ServerOptionsMenu[36].status = IT_STRING | IT_CVAR;
OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR;
}
#endif

View File

@ -760,26 +760,102 @@ static void P_NetUnArchiveColormaps(void)
#define LD_S2BOTTEX 0x04
#define LD_S2MIDTEX 0x08
static void P_NetArchiveWorld(void)
#define FD_FLAGS 0x01
#define FD_ALPHA 0x02
// Check if any of the sector's FOFs differ from how they spawned
static boolean CheckFFloorDiff(const sector_t *ss)
{
ffloor_t *rover;
for (rover = ss->ffloors; rover; rover = rover->next)
{
if (rover->flags != rover->spawnflags
|| rover->alpha != rover->spawnalpha)
{
return true; // we found an FOF that changed!
// don't bother checking for more, we do that later
}
}
return false;
}
// Special case: save the stats of all modified ffloors along with their ffloor "number"s
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
static void ArchiveFFloors(const sector_t *ss)
{
size_t j = 0; // ss->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
ffloor_t *rover;
UINT8 fflr_diff;
for (rover = ss->ffloors; rover; rover = rover->next)
{
fflr_diff = 0; // reset diff flags
if (rover->flags != rover->spawnflags)
fflr_diff |= FD_FLAGS;
if (rover->alpha != rover->spawnalpha)
fflr_diff |= FD_ALPHA;
if (fflr_diff)
{
WRITEUINT16(save_p, j); // save ffloor "number"
WRITEUINT8(save_p, fflr_diff);
if (fflr_diff & FD_FLAGS)
WRITEUINT32(save_p, rover->flags);
if (fflr_diff & FD_ALPHA)
WRITEINT16(save_p, rover->alpha);
}
j++;
}
WRITEUINT16(save_p, 0xffff);
}
static void UnArchiveFFloors(const sector_t *ss)
{
UINT16 j = 0; // number of current ffloor in loop
UINT16 fflr_i; // saved ffloor "number" of next modified ffloor
UINT16 fflr_diff; // saved ffloor diff
ffloor_t *rover;
rover = ss->ffloors;
if (!rover) // it is assumed sectors[i].ffloors actually exists, but just in case...
I_Error("Sector does not have any ffloors!");
fflr_i = READUINT16(save_p); // get first modified ffloor's number ready
for (;;) // for some reason the usual for (rover = x; ...) thing doesn't work here?
{
if (fflr_i == 0xffff) // end of modified ffloors list, let's stop already
break;
// should NEVER need to be checked
//if (rover == NULL)
//break;
if (j != fflr_i) // this ffloor was not modified
{
j++;
rover = rover->next;
continue;
}
fflr_diff = READUINT8(save_p);
if (fflr_diff & FD_FLAGS)
rover->flags = READUINT32(save_p);
if (fflr_diff & FD_ALPHA)
rover->alpha = READINT16(save_p);
fflr_i = READUINT16(save_p); // get next ffloor "number" ready
j++;
rover = rover->next;
}
}
static void ArchiveSectors(void)
{
size_t i;
INT32 statsec = 0, statline = 0;
const line_t *li = lines;
const line_t *spawnli = spawnlines;
const side_t *si;
const side_t *spawnsi;
UINT8 *put;
const sector_t *ss = sectors;
const sector_t *spawnss = spawnsectors;
UINT8 diff, diff2, diff3;
// initialize colormap vars because paranoia
ClearNetColormaps();
WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
put = save_p;
for (i = 0; i < numsectors; i++, ss++, spawnss++)
{
diff = diff2 = diff3 = 0;
@ -823,20 +899,8 @@ static void P_NetArchiveWorld(void)
if (ss->crumblestate)
diff3 |= SD_CRUMBLESTATE;
// Check if any of the sector's FOFs differ from how they spawned
if (ss->ffloors)
{
ffloor_t *rover;
for (rover = ss->ffloors; rover; rover = rover->next)
{
if (rover->flags != rover->spawnflags
|| rover->alpha != rover->spawnalpha)
{
diff |= SD_FFLOORS; // we found an FOF that changed!
break; // don't bother checking for more, we do that later
}
}
}
if (ss->ffloors && CheckFFloorDiff(ss))
diff |= SD_FFLOORS;
if (diff3)
diff2 |= SD_DIFF3;
@ -846,89 +910,142 @@ static void P_NetArchiveWorld(void)
if (diff)
{
statsec++;
WRITEUINT16(put, i);
WRITEUINT8(put, diff);
WRITEUINT16(save_p, i);
WRITEUINT8(save_p, diff);
if (diff & SD_DIFF2)
WRITEUINT8(put, diff2);
WRITEUINT8(save_p, diff2);
if (diff2 & SD_DIFF3)
WRITEUINT8(put, diff3);
WRITEUINT8(save_p, diff3);
if (diff & SD_FLOORHT)
WRITEFIXED(put, ss->floorheight);
WRITEFIXED(save_p, ss->floorheight);
if (diff & SD_CEILHT)
WRITEFIXED(put, ss->ceilingheight);
WRITEFIXED(save_p, ss->ceilingheight);
if (diff & SD_FLOORPIC)
WRITEMEM(put, levelflats[ss->floorpic].name, 8);
WRITEMEM(save_p, levelflats[ss->floorpic].name, 8);
if (diff & SD_CEILPIC)
WRITEMEM(put, levelflats[ss->ceilingpic].name, 8);
WRITEMEM(save_p, levelflats[ss->ceilingpic].name, 8);
if (diff & SD_LIGHT)
WRITEINT16(put, ss->lightlevel);
WRITEINT16(save_p, ss->lightlevel);
if (diff & SD_SPECIAL)
WRITEINT16(put, ss->special);
WRITEINT16(save_p, ss->special);
if (diff2 & SD_FXOFFS)
WRITEFIXED(put, ss->floor_xoffs);
WRITEFIXED(save_p, ss->floor_xoffs);
if (diff2 & SD_FYOFFS)
WRITEFIXED(put, ss->floor_yoffs);
WRITEFIXED(save_p, ss->floor_yoffs);
if (diff2 & SD_CXOFFS)
WRITEFIXED(put, ss->ceiling_xoffs);
WRITEFIXED(save_p, ss->ceiling_xoffs);
if (diff2 & SD_CYOFFS)
WRITEFIXED(put, ss->ceiling_yoffs);
WRITEFIXED(save_p, ss->ceiling_yoffs);
if (diff2 & SD_FLOORANG)
WRITEANGLE(put, ss->floorpic_angle);
WRITEANGLE(save_p, ss->floorpic_angle);
if (diff2 & SD_CEILANG)
WRITEANGLE(put, ss->ceilingpic_angle);
WRITEANGLE(save_p, ss->ceilingpic_angle);
if (diff2 & SD_TAG) // save only the tag
WRITEINT16(put, ss->tag);
WRITEINT16(save_p, ss->tag);
if (diff3 & SD_TAGLIST) // save both firsttag and nexttag
{ // either of these could be changed even if tag isn't
WRITEINT32(put, ss->firsttag);
WRITEINT32(put, ss->nexttag);
WRITEINT32(save_p, ss->firsttag);
WRITEINT32(save_p, ss->nexttag);
}
if (diff3 & SD_COLORMAP)
WRITEUINT32(put, CheckAddNetColormapToList(ss->extra_colormap));
WRITEUINT32(save_p, CheckAddNetColormapToList(ss->extra_colormap));
// returns existing index if already added, or appends to net_colormaps and returns new index
if (diff3 & SD_CRUMBLESTATE)
WRITEINT32(put, ss->crumblestate);
// Special case: save the stats of all modified ffloors along with their ffloor "number"s
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
WRITEINT32(save_p, ss->crumblestate);
if (diff & SD_FFLOORS)
{
size_t j = 0; // ss->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc
ffloor_t *rover;
UINT8 fflr_diff;
for (rover = ss->ffloors; rover; rover = rover->next)
{
fflr_diff = 0; // reset diff flags
if (rover->flags != rover->spawnflags)
fflr_diff |= 1;
if (rover->alpha != rover->spawnalpha)
fflr_diff |= 2;
if (fflr_diff)
{
WRITEUINT16(put, j); // save ffloor "number"
WRITEUINT8(put, fflr_diff);
if (fflr_diff & 1)
WRITEUINT32(put, rover->flags);
if (fflr_diff & 2)
WRITEINT16(put, rover->alpha);
}
j++;
}
WRITEUINT16(put, 0xffff);
}
ArchiveFFloors(ss);
}
}
WRITEUINT16(put, 0xffff);
WRITEUINT16(save_p, 0xffff);
}
static void UnArchiveSectors(void)
{
UINT16 i;
UINT8 diff, diff2, diff3;
for (;;)
{
i = READUINT16(save_p);
if (i == 0xffff)
break;
if (i > numsectors)
I_Error("Invalid sector number %u from server (expected end at %s)", i, sizeu1(numsectors));
diff = READUINT8(save_p);
if (diff & SD_DIFF2)
diff2 = READUINT8(save_p);
else
diff2 = 0;
if (diff2 & SD_DIFF3)
diff3 = READUINT8(save_p);
else
diff3 = 0;
if (diff & SD_FLOORHT)
sectors[i].floorheight = READFIXED(save_p);
if (diff & SD_CEILHT)
sectors[i].ceilingheight = READFIXED(save_p);
if (diff & SD_FLOORPIC)
{
sectors[i].floorpic = P_AddLevelFlatRuntime((char *)save_p);
save_p += 8;
}
if (diff & SD_CEILPIC)
{
sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)save_p);
save_p += 8;
}
if (diff & SD_LIGHT)
sectors[i].lightlevel = READINT16(save_p);
if (diff & SD_SPECIAL)
sectors[i].special = READINT16(save_p);
if (diff2 & SD_FXOFFS)
sectors[i].floor_xoffs = READFIXED(save_p);
if (diff2 & SD_FYOFFS)
sectors[i].floor_yoffs = READFIXED(save_p);
if (diff2 & SD_CXOFFS)
sectors[i].ceiling_xoffs = READFIXED(save_p);
if (diff2 & SD_CYOFFS)
sectors[i].ceiling_yoffs = READFIXED(save_p);
if (diff2 & SD_FLOORANG)
sectors[i].floorpic_angle = READANGLE(save_p);
if (diff2 & SD_CEILANG)
sectors[i].ceilingpic_angle = READANGLE(save_p);
if (diff2 & SD_TAG)
sectors[i].tag = READINT16(save_p); // DON'T use P_ChangeSectorTag
if (diff3 & SD_TAGLIST)
{
sectors[i].firsttag = READINT32(save_p);
sectors[i].nexttag = READINT32(save_p);
}
if (diff3 & SD_COLORMAP)
sectors[i].extra_colormap = GetNetColormapFromList(READUINT32(save_p));
if (diff3 & SD_CRUMBLESTATE)
sectors[i].crumblestate = READINT32(save_p);
if (diff & SD_FFLOORS)
UnArchiveFFloors(&sectors[i]);
}
}
static void ArchiveLines(void)
{
size_t i;
const line_t *li = lines;
const line_t *spawnli = spawnlines;
const side_t *si;
const side_t *spawnsi;
UINT8 diff, diff2; // no diff3
// do lines
for (i = 0; i < numlines; i++, spawnli++, li++)
{
diff = diff2 = diff3 = 0;
diff = diff2 = 0;
if (li->special != spawnli->special)
diff |= LD_SPECIAL;
@ -968,52 +1085,109 @@ static void P_NetArchiveWorld(void)
if (diff)
{
statline++;
WRITEINT16(put, i);
WRITEUINT8(put, diff);
WRITEINT16(save_p, i);
WRITEUINT8(save_p, diff);
if (diff & LD_DIFF2)
WRITEUINT8(put, diff2);
WRITEUINT8(save_p, diff2);
if (diff & LD_FLAG)
WRITEINT16(put, li->flags);
WRITEINT16(save_p, li->flags);
if (diff & LD_SPECIAL)
WRITEINT16(put, li->special);
WRITEINT16(save_p, li->special);
if (diff & LD_CLLCOUNT)
WRITEINT16(put, li->callcount);
WRITEINT16(save_p, li->callcount);
si = &sides[li->sidenum[0]];
if (diff & LD_S1TEXOFF)
WRITEFIXED(put, si->textureoffset);
WRITEFIXED(save_p, si->textureoffset);
if (diff & LD_S1TOPTEX)
WRITEINT32(put, si->toptexture);
WRITEINT32(save_p, si->toptexture);
if (diff & LD_S1BOTTEX)
WRITEINT32(put, si->bottomtexture);
WRITEINT32(save_p, si->bottomtexture);
if (diff & LD_S1MIDTEX)
WRITEINT32(put, si->midtexture);
WRITEINT32(save_p, si->midtexture);
si = &sides[li->sidenum[1]];
if (diff2 & LD_S2TEXOFF)
WRITEFIXED(put, si->textureoffset);
WRITEFIXED(save_p, si->textureoffset);
if (diff2 & LD_S2TOPTEX)
WRITEINT32(put, si->toptexture);
WRITEINT32(save_p, si->toptexture);
if (diff2 & LD_S2BOTTEX)
WRITEINT32(put, si->bottomtexture);
WRITEINT32(save_p, si->bottomtexture);
if (diff2 & LD_S2MIDTEX)
WRITEINT32(put, si->midtexture);
WRITEINT32(save_p, si->midtexture);
}
}
WRITEUINT16(put, 0xffff);
R_ClearTextureNumCache(false);
WRITEUINT16(save_p, 0xffff);
}
save_p = put;
static void UnArchiveLines(void)
{
UINT16 i;
line_t *li;
side_t *si;
UINT8 diff, diff2; // no diff3
for (;;)
{
i = READUINT16(save_p);
if (i == 0xffff)
break;
if (i > numlines)
I_Error("Invalid line number %u from server", i);
diff = READUINT8(save_p);
li = &lines[i];
if (diff & LD_DIFF2)
diff2 = READUINT8(save_p);
else
diff2 = 0;
if (diff & LD_FLAG)
li->flags = READINT16(save_p);
if (diff & LD_SPECIAL)
li->special = READINT16(save_p);
if (diff & LD_CLLCOUNT)
li->callcount = READINT16(save_p);
si = &sides[li->sidenum[0]];
if (diff & LD_S1TEXOFF)
si->textureoffset = READFIXED(save_p);
if (diff & LD_S1TOPTEX)
si->toptexture = READINT32(save_p);
if (diff & LD_S1BOTTEX)
si->bottomtexture = READINT32(save_p);
if (diff & LD_S1MIDTEX)
si->midtexture = READINT32(save_p);
si = &sides[li->sidenum[1]];
if (diff2 & LD_S2TEXOFF)
si->textureoffset = READFIXED(save_p);
if (diff2 & LD_S2TOPTEX)
si->toptexture = READINT32(save_p);
if (diff2 & LD_S2BOTTEX)
si->bottomtexture = READINT32(save_p);
if (diff2 & LD_S2MIDTEX)
si->midtexture = READINT32(save_p);
}
}
static void P_NetArchiveWorld(void)
{
// initialize colormap vars because paranoia
ClearNetColormaps();
WRITEUINT32(save_p, ARCHIVEBLOCK_WORLD);
ArchiveSectors();
ArchiveLines();
R_ClearTextureNumCache(false);
}
static void P_NetUnArchiveWorld(void)
{
UINT16 i;
line_t *li;
side_t *si;
UINT8 *get;
UINT8 diff, diff2, diff3;
if (READUINT32(save_p) != ARCHIVEBLOCK_WORLD)
I_Error("Bad $$$.sav at archive block World");
@ -1029,161 +1203,8 @@ static void P_NetUnArchiveWorld(void)
num_ffloors++;
}
get = save_p;
for (;;)
{
i = READUINT16(get);
if (i == 0xffff)
break;
if (i > numsectors)
I_Error("Invalid sector number %u from server (expected end at %s)", i, sizeu1(numsectors));
diff = READUINT8(get);
if (diff & SD_DIFF2)
diff2 = READUINT8(get);
else
diff2 = 0;
if (diff2 & SD_DIFF3)
diff3 = READUINT8(get);
else
diff3 = 0;
if (diff & SD_FLOORHT)
sectors[i].floorheight = READFIXED(get);
if (diff & SD_CEILHT)
sectors[i].ceilingheight = READFIXED(get);
if (diff & SD_FLOORPIC)
{
sectors[i].floorpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_CEILPIC)
{
sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_LIGHT)
sectors[i].lightlevel = READINT16(get);
if (diff & SD_SPECIAL)
sectors[i].special = READINT16(get);
if (diff2 & SD_FXOFFS)
sectors[i].floor_xoffs = READFIXED(get);
if (diff2 & SD_FYOFFS)
sectors[i].floor_yoffs = READFIXED(get);
if (diff2 & SD_CXOFFS)
sectors[i].ceiling_xoffs = READFIXED(get);
if (diff2 & SD_CYOFFS)
sectors[i].ceiling_yoffs = READFIXED(get);
if (diff2 & SD_FLOORANG)
sectors[i].floorpic_angle = READANGLE(get);
if (diff2 & SD_CEILANG)
sectors[i].ceilingpic_angle = READANGLE(get);
if (diff2 & SD_TAG)
sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
if (diff3 & SD_TAGLIST)
{
sectors[i].firsttag = READINT32(get);
sectors[i].nexttag = READINT32(get);
}
if (diff3 & SD_COLORMAP)
sectors[i].extra_colormap = GetNetColormapFromList(READUINT32(get));
if (diff3 & SD_CRUMBLESTATE)
sectors[i].crumblestate = READINT32(get);
if (diff & SD_FFLOORS)
{
UINT16 j = 0; // number of current ffloor in loop
UINT16 fflr_i; // saved ffloor "number" of next modified ffloor
UINT16 fflr_diff; // saved ffloor diff
ffloor_t *rover;
rover = sectors[i].ffloors;
if (!rover) // it is assumed sectors[i].ffloors actually exists, but just in case...
I_Error("Sector does not have any ffloors!");
fflr_i = READUINT16(get); // get first modified ffloor's number ready
for (;;) // for some reason the usual for (rover = x; ...) thing doesn't work here?
{
if (fflr_i == 0xffff) // end of modified ffloors list, let's stop already
break;
// should NEVER need to be checked
//if (rover == NULL)
//break;
if (j != fflr_i) // this ffloor was not modified
{
j++;
rover = rover->next;
continue;
}
fflr_diff = READUINT8(get);
if (fflr_diff & 1)
rover->flags = READUINT32(get);
if (fflr_diff & 2)
rover->alpha = READINT16(get);
fflr_i = READUINT16(get); // get next ffloor "number" ready
j++;
rover = rover->next;
}
}
}
for (;;)
{
i = READUINT16(get);
if (i == 0xffff)
break;
if (i > numlines)
I_Error("Invalid line number %u from server", i);
diff = READUINT8(get);
li = &lines[i];
if (diff & LD_DIFF2)
diff2 = READUINT8(get);
else
diff2 = 0;
diff3 = 0;
if (diff & LD_FLAG)
li->flags = READINT16(get);
if (diff & LD_SPECIAL)
li->special = READINT16(get);
if (diff & LD_CLLCOUNT)
li->callcount = READINT16(get);
si = &sides[li->sidenum[0]];
if (diff & LD_S1TEXOFF)
si->textureoffset = READFIXED(get);
if (diff & LD_S1TOPTEX)
si->toptexture = READINT32(get);
if (diff & LD_S1BOTTEX)
si->bottomtexture = READINT32(get);
if (diff & LD_S1MIDTEX)
si->midtexture = READINT32(get);
si = &sides[li->sidenum[1]];
if (diff2 & LD_S2TEXOFF)
si->textureoffset = READFIXED(get);
if (diff2 & LD_S2TOPTEX)
si->toptexture = READINT32(get);
if (diff2 & LD_S2BOTTEX)
si->bottomtexture = READINT32(get);
if (diff2 & LD_S2MIDTEX)
si->midtexture = READINT32(get);
}
save_p = get;
UnArchiveSectors();
UnArchiveLines();
}
//
@ -1474,42 +1495,14 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
if (diff2 & MD2_FLOORROVER)
{
ffloor_t *rover;
size_t i = 0;
UINT32 roverindex = 0;
for (rover = mobj->floorrover->target->ffloors; rover; rover = rover->next)
{
if (rover == mobj->floorrover)
{
roverindex = i;
break;
}
i++;
}
WRITEUINT32(save_p, (UINT32)(mobj->floorrover->target - sectors));
WRITEUINT32(save_p, rover ? roverindex : i); // store max index to denote invalid ffloor ref
WRITEUINT32(save_p, SaveSector(mobj->floorrover->target));
WRITEUINT16(save_p, P_GetFFloorID(mobj->floorrover));
}
if (diff2 & MD2_CEILINGROVER)
{
ffloor_t *rover;
size_t i = 0;
UINT32 roverindex = 0;
for (rover = mobj->ceilingrover->target->ffloors; rover; rover = rover->next)
{
if (rover == mobj->ceilingrover)
{
roverindex = i;
break;
}
i++;
}
WRITEUINT32(save_p, (UINT32)(mobj->ceilingrover->target - sectors));
WRITEUINT32(save_p, rover ? roverindex : i); // store max index to denote invalid ffloor ref
WRITEUINT32(save_p, SaveSector(mobj->ceilingrover->target));
WRITEUINT16(save_p, P_GetFFloorID(mobj->ceilingrover));
}
if (diff & MD_SPAWNPOINT)
@ -2431,34 +2424,16 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
if (diff2 & MD2_FLOORROVER)
{
size_t floor_sectornum = (size_t)READUINT32(save_p);
size_t floor_rovernum = (size_t)READUINT32(save_p);
ffloor_t *rover = NULL;
size_t rovernum = 0;
for (rover = sectors[floor_sectornum].ffloors; rover; rover = rover->next)
{
if (rovernum == floor_rovernum)
break;
rovernum++;
}
floorrover = rover;
sector_t *sec = LoadSector(READUINT32(save_p));
UINT16 id = READUINT16(save_p);
floorrover = P_GetFFloorByID(sec, id);
}
if (diff2 & MD2_CEILINGROVER)
{
size_t ceiling_sectornum = (size_t)READUINT32(save_p);
size_t ceiling_rovernum = (size_t)READUINT32(save_p);
ffloor_t *rover = NULL;
size_t rovernum = 0;
for (rover = sectors[ceiling_sectornum].ffloors; rover; rover = rover->next)
{
if (rovernum == ceiling_rovernum)
break;
rovernum++;
}
ceilingrover = rover;
sector_t *sec = LoadSector(READUINT32(save_p));
UINT16 id = READUINT16(save_p);
ceilingrover = P_GetFFloorByID(sec, id);
}
if (diff & MD_SPAWNPOINT)
@ -2702,6 +2677,10 @@ static thinker_t* LoadBounceCheeseThinker(actionf_p1 thinker)
ht->floorwasheight = READFIXED(save_p);
ht->ceilingwasheight = READFIXED(save_p);
ht->low = READCHAR(save_p);
if (ht->sector)
ht->sector->ceilingdata = ht;
return &ht->thinker;
}
@ -2715,6 +2694,13 @@ static thinker_t* LoadContinuousFallThinker(actionf_p1 thinker)
ht->floorstartheight = READFIXED(save_p);
ht->ceilingstartheight = READFIXED(save_p);
ht->destheight = READFIXED(save_p);
if (ht->sector)
{
ht->sector->ceilingdata = ht;
ht->sector->floordata = ht;
}
return &ht->thinker;
}
@ -2728,6 +2714,13 @@ static thinker_t* LoadMarioBlockThinker(actionf_p1 thinker)
ht->floorstartheight = READFIXED(save_p);
ht->ceilingstartheight = READFIXED(save_p);
ht->tag = READINT16(save_p);
if (ht->sector)
{
ht->sector->ceilingdata = ht;
ht->sector->floordata = ht;
}
return &ht->thinker;
}
@ -2754,6 +2747,13 @@ static thinker_t* LoadThwompThinker(actionf_p1 thinker)
ht->delay = READINT32(save_p);
ht->tag = READINT16(save_p);
ht->sound = READUINT16(save_p);
if (ht->sector)
{
ht->sector->ceilingdata = ht;
ht->sector->floordata = ht;
}
return &ht->thinker;
}

View File

@ -5678,6 +5678,35 @@ void P_UpdateSpecials(void)
}
}
//
// Floor over floors (FOFs), 3Dfloors, 3Dblocks, fake floors (ffloors), rovers, or whatever you want to call them
//
/** Gets the ID number for a 3Dfloor in its target sector.
*
* \param fflr The 3Dfloor we want an ID for.
* \return ID of 3Dfloor in target sector. Note that the first FOF's ID is 0. UINT16_MAX is given if invalid.
* \sa P_GetFFloorByID
*/
UINT16 P_GetFFloorID(ffloor_t *fflr)
{
ffloor_t *rover;
sector_t *sec;
UINT16 i = 0;
if (!fflr)
return UINT16_MAX;
sec = fflr->target;
if (!sec->ffloors)
return UINT16_MAX;
for (rover = sec->ffloors; rover; rover = rover->next, i++)
if (rover == fflr)
return i;
return UINT16_MAX;
}
/** Gets a 3Dfloor by control sector.
*
* \param sec Target sector.
@ -5702,7 +5731,7 @@ static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2)
* \param sec Target sector.
* \param id ID of 3Dfloor in target sector. Note that the first FOF's ID is 0.
* \return Pointer to found 3Dfloor, or NULL.
* \sa P_GetFFloorBySec
* \sa P_GetFFloorBySec, P_GetFFloorID
*/
ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id)
{
@ -6013,7 +6042,7 @@ static void P_AddBlockThinker(sector_t *sec, line_t *sourceline)
* \sa P_SpawnSpecials, T_RaiseSector
* \author SSNTails <http://www.ssntails.org>
*/
static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline, boolean lower, boolean spindash)
static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline, fixed_t speed, fixed_t ceilingtop, fixed_t ceilingbottom, boolean lower, boolean spindash)
{
raise_t *raise;
@ -6025,10 +6054,10 @@ static void P_AddRaiseThinker(sector_t *sec, line_t *sourceline, boolean lower,
raise->sourceline = sourceline;
raise->sector = sec;
raise->ceilingtop = P_FindHighestCeilingSurrounding(sec);
raise->ceilingbottom = P_FindLowestCeilingSurrounding(sec);
raise->ceilingtop = ceilingtop;
raise->ceilingbottom = ceilingbottom;
raise->basespeed = FixedDiv(P_AproxDistance(sourceline->dx, sourceline->dy), 4*FRACUNIT);
raise->basespeed = speed;
if (lower)
raise->flags |= RF_REVERSE;
@ -6938,44 +6967,32 @@ void P_SpawnSpecials(boolean fromnetsave)
break;
case 190: // Rising Platform FOF (solid, opaque, shadows)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
case 191: // Rising Platform FOF (solid, opaque, no shadows)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_NOSHADE|FF_CUTLEVEL, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
case 192: // Rising Platform TL block: FOF (solid, translucent)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_NOSHADE|FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
case 193: // Rising Platform FOF (solid, invisible)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_NOSHADE, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
case 194: // Rising Platform 'Platform' - You can jump up through it
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_PLATFORM|FF_BOTHPLANES|FF_ALLSIDES;
if (lines[i].flags & ML_NOCLIMB)
ffloorflags |= FF_NOSHADE;
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
case 195: // Rising Platform Translucent "platform"
// If line has no-climb set, don't give it shadows, otherwise do
ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_PLATFORM|FF_TRANSLUCENT|FF_BOTHPLANES|FF_ALLSIDES|FF_EXTRA|FF_CUTEXTRA;
if (lines[i].flags & ML_NOCLIMB)
ffloorflags |= FF_NOSHADE;
{
fixed_t speed = FixedDiv(P_AproxDistance(lines[i].dx, lines[i].dy), 4*FRACUNIT);
fixed_t ceilingtop = P_FindHighestCeilingSurrounding(lines[i].frontsector);
fixed_t ceilingbottom = P_FindLowestCeilingSurrounding(lines[i].frontsector);
ffloorflags = FF_EXISTS|FF_SOLID;
if (lines[i].special != 193)
ffloorflags |= FF_RENDERALL;
if (lines[i].special <= 191)
ffloorflags |= FF_CUTLEVEL;
if (lines[i].special == 192 || lines[i].special == 195)
ffloorflags |= FF_TRANSLUCENT|FF_EXTRA|FF_CUTEXTRA;
if (lines[i].special >= 194)
ffloorflags |= FF_PLATFORM|FF_BOTHPLANES|FF_ALLSIDES;
if (lines[i].special != 190 && (lines[i].special <= 193 || lines[i].flags & ML_NOCLIMB))
ffloorflags |= FF_NOSHADE;
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
P_AddRaiseThinker(lines[i].frontsector, &lines[i], !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
P_AddRaiseThinker(lines[i].frontsector, &lines[i], speed, ceilingtop, ceilingbottom, !!(lines[i].flags & ML_BLOCKMONSTERS), !!(lines[i].flags & ML_NOCLIMB));
break;
}
case 200: // Double light effect
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_CUTSPRITES|FF_DOUBLESHADOW, secthinkers);

View File

@ -74,6 +74,7 @@ void P_RunDeNightserizeExecutors(mobj_t *actor);
void P_RunNightsLapExecutors(mobj_t *actor);
void P_RunNightsCapsuleTouchExecutors(mobj_t *actor, boolean entering, boolean enoughspheres);
UINT16 P_GetFFloorID(ffloor_t *fflr);
ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id);
//

View File

@ -372,7 +372,9 @@ static boolean IgnoreMouse(void)
return false;
if (menuactive)
return !M_MouseNeeded();
if (paused || con_destlines || chat_on || gamestate != GS_LEVEL)
if (paused || con_destlines || chat_on)
return true;
if (gamestate != GS_LEVEL && gamestate != GS_INTERMISSION && gamestate != GS_CUTSCENE)
return true;
return false;
}

View File

@ -920,6 +920,40 @@ const char *W_CheckNameForNum(lumpnum_t lumpnum)
// 'startlump' is the lump number to start the search
//
UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump)
{
UINT16 i;
static char uname[8 + 1];
if (!TestValidLump(wad,0))
return INT16_MAX;
strlcpy(uname, name, sizeof uname);
strupr(uname);
//
// scan forward
// start at 'startlump', useful parameter when there are multiple
// resources with the same name
//
if (startlump < wadfiles[wad]->numlumps)
{
lumpinfo_t *lump_p = wadfiles[wad]->lumpinfo + startlump;
for (i = startlump; i < wadfiles[wad]->numlumps; i++, lump_p++)
if (!strncmp(lump_p->name, uname, sizeof(uname) - 1))
return i;
}
// not found.
return INT16_MAX;
}
//
// Like W_CheckNumForNamePwad, but can find entries with long names
//
// Should be the only version, but that's not possible until we fix
// all the instances of non null-terminated strings in the codebase...
//
UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump)
{
UINT16 i;
static char uname[256 + 1];
@ -1025,7 +1059,8 @@ lumpnum_t W_CheckNumForName(const char *name)
// most recent entries first
for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--)
{
if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0)
if (!lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname[8]
&& strncmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name, 8) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
@ -1045,13 +1080,63 @@ lumpnum_t W_CheckNumForName(const char *name)
{
// Update the cache.
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
strncpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 8);
lumpnumcache[lumpnumcacheindex].lumpnum = (i<<16)+check;
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
//
// Like W_CheckNumForName, but can find entries with long names
//
// Should be the only version, but that's not possible until we fix
// all the instances of non null-terminated strings in the codebase...
//
lumpnum_t W_CheckNumForLongName(const char *name)
{
INT32 i;
lumpnum_t check = INT16_MAX;
if (!*name) // some doofus gave us an empty string?
return LUMPERROR;
// Check the lumpnumcache first. Loop backwards so that we check
// most recent entries first
for (i = lumpnumcacheindex + LUMPNUMCACHESIZE; i > lumpnumcacheindex; i--)
{
if (strcmp(lumpnumcache[i & (LUMPNUMCACHESIZE - 1)].lumpname, name) == 0)
{
lumpnumcacheindex = i & (LUMPNUMCACHESIZE - 1);
return lumpnumcache[lumpnumcacheindex].lumpnum;
}
}
// scan wad files backwards so patch lump files take precedence
for (i = numwadfiles - 1; i >= 0; i--)
{
check = W_CheckNumForLongNamePwad(name,(UINT16)i,0);
if (check != INT16_MAX)
break; //found it
}
if (check == INT16_MAX) return LUMPERROR;
else
{
if (strlen(name) < 32)
{
// Update the cache.
lumpnumcacheindex = (lumpnumcacheindex + 1) & (LUMPNUMCACHESIZE - 1);
memset(lumpnumcache[lumpnumcacheindex].lumpname, '\0', 32);
strlcpy(lumpnumcache[lumpnumcacheindex].lumpname, name, 32);
lumpnumcache[lumpnumcacheindex].lumpnum = (i << 16) + check;
}
return (i << 16) + check;
}
}
// Look for valid map data through all added files in descendant order.
// Get a map marker for WADs, and a standalone WAD file lump inside PK3s.
// TODO: Make it search through cache first, maybe...?
@ -1100,6 +1185,24 @@ lumpnum_t W_GetNumForName(const char *name)
return i;
}
//
// Like W_GetNumForName, but can find entries with long names
//
// Should be the only version, but that's not possible until we fix
// all the instances of non null-terminated strings in the codebase...
//
lumpnum_t W_GetNumForLongName(const char *name)
{
lumpnum_t i;
i = W_CheckNumForLongName(name);
if (i == LUMPERROR)
I_Error("W_GetNumForLongName: %s not found!\n", name);
return i;
}
//
// W_CheckNumForNameInBlock
// Checks only in blocks from blockstart lump to blockend lump
@ -1139,7 +1242,7 @@ UINT8 W_LumpExists(const char *name)
{
lumpinfo_t *lump_p = wadfiles[i]->lumpinfo;
for (j = 0; j < wadfiles[i]->numlumps; ++j, ++lump_p)
if (fastcmp(lump_p->name,name))
if (fastcmp(lump_p->longname, name))
return true;
}
return false;
@ -1680,6 +1783,17 @@ void *W_CachePatchName(const char *name, INT32 tag)
return W_CachePatchNum(W_GetNumForName("MISSING"), tag);
return W_CachePatchNum(num, tag);
}
void *W_CachePatchLongName(const char *name, INT32 tag)
{
lumpnum_t num;
num = W_CheckNumForLongName(name);
if (num == LUMPERROR)
return W_CachePatchNum(W_GetNumForLongName("MISSING"), tag);
return W_CachePatchNum(num, tag);
}
#ifndef NOMD5
#define MD5_LEN 16

View File

@ -156,6 +156,7 @@ const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
const char *W_CheckNameForNum(lumpnum_t lumpnum);
UINT16 W_CheckNumForNamePwad(const char *name, UINT16 wad, UINT16 startlump); // checks only in one pwad
UINT16 W_CheckNumForLongNamePwad(const char *name, UINT16 wad, UINT16 startlump);
/* Find the first lump after F_START for instance. */
UINT16 W_CheckNumForMarkerStartPwad(const char *name, UINT16 wad, UINT16 startlump);
@ -166,7 +167,9 @@ UINT16 W_CheckNumForFolderEndPK3(const char *name, UINT16 wad, UINT16 startlump)
lumpnum_t W_CheckNumForMap(const char *name);
lumpnum_t W_CheckNumForName(const char *name);
lumpnum_t W_CheckNumForLongName(const char *name);
lumpnum_t W_GetNumForName(const char *name); // like W_CheckNumForName but I_Error on LUMPERROR
lumpnum_t W_GetNumForLongName(const char *name);
lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, const char *blockend);
UINT8 W_LumpExists(const char *name); // Lua uses this.
@ -194,6 +197,7 @@ boolean W_IsPatchCached(lumpnum_t lump, void *ptr);
void *W_CacheLumpName(const char *name, INT32 tag);
void *W_CachePatchName(const char *name, INT32 tag);
void *W_CachePatchLongName(const char *name, INT32 tag);
// Returns either a Software patch, or an OpenGL patch.
// Performs any necessary conversions from PNG images.