Add some extension checks to avoid problems when running menusys in engines that lack support for various features...

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5812 fc73d0e0-1445-4013-8a0c-d673dee63da5
This commit is contained in:
Spoike 2021-04-08 19:16:34 +00:00
parent d855cc6e84
commit cd50a54a5a
14 changed files with 3836 additions and 3609 deletions

View File

@ -6,8 +6,7 @@
#define CSQC //select the module
#ifdef CSQC_SIMPLE
#include "qsextensions.qc" //also sets up system defs
#undef CSQC_SIMPLE
#include "fteextensions.qc" //extra stuff...
#include "fteextensions.qc" //extra stuff
#else
#include "fteextensions.qc" //also sets up system defs
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,10 @@
#pragma progs_dat "../menu.dat"
#pragma target fte
//#pragma target fte
#define MENU //select the module
#include "fteextensions.qc" //also sets up system defs
#includelist
fteextensions.qc //also sets up system defs
menusys/mitems.qc //root item type
menusys/mitems_common.qc //basic types
menusys/mitem_desktop.qc //other sort of root item

View File

@ -53,6 +53,7 @@ helper functions to avoid blowing up in older clients.
#endif
float(string cmd, float assumption) checkcommand2 =
{
__using checkcommand;
if (!checkextension("FTE_QC_CHECKCOMMAND"))
return assumption;
return checkcommand(cmd);
@ -63,6 +64,7 @@ float(__variant cmd, float assumption) checkbuiltin2 =
return assumption;
return checkbuiltin(cmd);
};
#define checkbuiltin2(c,a) ((a)?checkbuiltin2(c,a):checkbuiltin(c))
#ifdef CSQC
float() clientstate =

View File

@ -31,6 +31,8 @@ void(mitem_desktop desktop) M_Menu_Mods =
float mod;
for (mod = 0; ; mod++)
{
__using GGDI_DESCRIPTION, GGDI_LOADCOMMAND, GGDI_ICON;
string gamedir = getgamedirinfo(mod, GGDI_GAMEDIR);
if not(gamedir)
break;

View File

@ -6,7 +6,17 @@ static string newgameinfo;
nonstatic void(mitem_desktop desktop) M_Dir =
{ //implementing this primarily so its available in QSS. :P
string path = argv(1);
searchhandle h = search_begin(path, SB_FULLPACKAGEPATH, 0);
string pack = argv(2);
searchhandle h;
if (path == "")
{
print("m_dir <FILEPATH> [PACKAGE]\n");
return;
}
if (pack != "")
h = search_begin(path, SB_FULLPACKAGEPATH|SB_FORCESEARCH|16, 0, argv(2));
else
h = search_begin(path, SB_FULLPACKAGEPATH|16|32, 0);
print(sprintf("Directory listing of %S (%g files)\n", path, search_getsize(h)));
for (float i = 0; i < search_getsize(h); i++)
{
@ -32,68 +42,67 @@ nonstatic void(mitem_desktop desktop) M_Dir =
#define FOURCC(a,b,c,d) ((int)(a)<<0i)|((int)(b)<<8i)|((int)(c)<<16i)|((int)(d)<<24i)
static string(string name) getmapdesc =
{
filestream f;
if (!checkbuiltin(fread) || !checkbuiltin(fseek) || !checkbuiltin(memalloc) || !checkbuiltin(memgetval))
return ""; //we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.
else
if (checkbuiltin(fread) && checkbuiltin(fseek) && checkbuiltin(memalloc) && checkbuiltin(memfree) && checkbuiltin(memgetval) && checkbuiltin(itof) && checkbuiltin(ftoi))
{ //we can do it, so do it.
f = fopen(name, FILE_READ);
filestream f = fopen(name, FILE_READ);
if (f < 0)
print(sprintf("Unable to read %s\n", name));
}
name = substring(name, 5, -5);
if (f < 0)
return name;
name = substring(name, 5, -5);
if (f < 0)
return name;
int *header = memalloc(sizeof(int)*4);
int bspver = 0, entofs, entlen;
if (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(
bspver == FOURCC('I','B','S','P') || //IBSP (q2/q3)
bspver == FOURCC('R','B','S','P') || //RBSP (jk2o etc)
bspver == FOURCC('F','B','S','P') || //IBSP (qfusion/warsow)
bspver == 29i || //q1
bspver == 30i || //hl
bspver == FOURCC('B','S','P','2')))) //bsp2
{
if (bspver == FOURCC('I','B','S','P'))
{ //has an actual version number! ooo...
entofs = header[2];
entlen = header[3];
}
else
int *header = memalloc(sizeof(int)*4);
int bspver = 0, entofs, entlen;
if (fread(f, (void*)header, sizeof(int)*4) == sizeof(int)*4 && ((bspver=header[0]),(
bspver == FOURCC('I','B','S','P') || //IBSP (q2/q3)
bspver == FOURCC('R','B','S','P') || //RBSP (jk2o etc)
bspver == FOURCC('F','B','S','P') || //IBSP (qfusion/warsow)
bspver == 29i || //q1
bspver == 30i || //hl
bspver == FOURCC('B','S','P','2')))) //bsp2
{
entofs = header[1];
entlen = header[2];
}
fseek(f, entofs);
string s = (string)memalloc(entlen+1);
fread(f, (void*)s, entlen);
float argc = tokenize(s);
if (argv(0) == "{")
{
for (float p = 1; p < argc; p+=2)
{
string t = argv(p);
if (t == "message")
{ //finally found the human-readable name of the map. woo.
name = argv(p+1);
break;
}
if (t == "}") //don't read the message from some kind of trigger
break;
if (t == "{") //some sort of corruption
break;
if (bspver == FOURCC('I','B','S','P'))
{ //has an actual version number! ooo...
entofs = header[2];
entlen = header[3];
}
else
{
entofs = header[1];
entlen = header[2];
}
fseek(f, entofs);
string s = (string)memalloc(entlen+1);
fread(f, (void*)s, entlen);
float argc = tokenize(s);
if (argv(0) == "{")
{
for (float p = 1; p < argc; p+=2)
{
string t = argv(p);
if (t == "message")
{ //finally found the human-readable name of the map. woo.
name = argv(p+1);
break;
}
if (t == "}") //don't read the message from some kind of trigger
break;
if (t == "{") //some sort of corruption
break;
}
}
else
name = "ERROR";
memfree((void*)s);
}
else
name = "ERROR";
memfree((void*)s);
name = sprintf("UNSUPPORTED %i", bspver);
memfree(header);
fclose(f);
return name;
}
else
name = sprintf("UNSUPPORTED %i", bspver);
memfree(header);
fclose(f);
return name;
return ""; //we can't do that in this engine... don't show anything, because its pointless showing the same thing twice.
};
static string(string name) packagetogamedir =

View File

@ -2,7 +2,7 @@ class mitem_playerpreview : mitem_spinnymodel
{
virtual void(vector pos) item_draw =
{
if (checkbuiltin2(setcustomskin, FALSE))
if (checkbuiltin(setcustomskin))
{
//if you wanted to get more advanced, you could use q3 skins here.
if (cvar("noskins")==1)

View File

@ -7,7 +7,7 @@ no background tint, so the game is still visible so you can preview it.
nonstatic void(mitem_desktop desktop) M_Configs =
{
local float i;
float i;
mitem_exmenu m;
m = spawn(mitem_exmenu, item_text:_("Game Presets / Configs"), item_flags:IF_SELECTABLE, item_command:"m_options");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
@ -28,18 +28,22 @@ nonstatic void(mitem_desktop desktop) M_Configs =
float fs, y=0;
float c = 0;
fs = search_begin("configs/game_*.cfg", TRUE, TRUE);
float c = search_getsize(fs);
for (i = 0; i < c; i++)
if (fs >= 0)
{
string fname = search_getfilename(fs, i);
string iname = strzone(substring(fname, 13, -5));
string dname = GetFirstLineComment(fname, iname);
iname = sprintf("exec \"%s\"", fname);
if (dname && !fr.findchildcmd(iname))
fr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=16], '100 16');
c = search_getsize(fs);
for (i = 0; i < c; i++)
{
string fname = search_getfilename(fs, i);
string iname = strzone(substring(fname, 13, -5));
string dname = GetFirstLineComment(fname, iname);
iname = sprintf("exec \"%s\"", fname);
if (dname && !fr.findchildcmd(iname))
fr.add(spawn(mitem_text, item_text:dname, item_command:iname, item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=16], '100 16');
}
search_end(fs);
}
search_end(fs);
if (c <= 0)
fr.add(spawn(mitem_text, item_text:"No configs found", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y], '100 16');

View File

@ -6,6 +6,7 @@ nonstatic void(mitem_desktop desktop) M_Quit =
{
local float pos;
mitem_exmenu m;
float unsaved = FALSE;
m = spawn(mitem_exmenu, item_text:_("Options"), item_flags:IF_SELECTABLE, item_command:"m_main");
desktop.add(m, RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_PARENT_MAX, '0 0', '0 0');
desktop.item_focuschange(m, IF_KFOCUSED);
@ -18,7 +19,9 @@ nonstatic void(mitem_desktop desktop) M_Quit =
// mitem_pic banner = spawn(mitem_pic, item_text:"gfx/p_option.lmp", item_size_y:24, item_flags:IF_CENTERALIGN);
// m.add(banner, RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [(-160-banner.item_size_x)*0.5, pos-32], [(-160+banner.item_size_x)*0.5, pos-8]);
if (cvars_haveunsaved())
if (checkbuiltin(cvars_haveunsaved))
unsaved = cvars_haveunsaved(); //engines that don't have this are assumed to always save regardless. Which makes prompting for it irrelevant. The all-saved text ignores saving entirely so it still makes sense.
if (unsaved)
{
m.add(spawn(mitem_text, item_text:"Save configuration?", item_scale:16, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-160, pos-(8+16)], [0, pos-8]); pos += 16;

View File

@ -27,7 +27,7 @@ var float autocvar_sb_showname = 1;
#define COLUMN_FRAGLIMIT COLUMN(4*8, fraglimit, "FL", ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_fraglimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_TIMELIMIT COLUMN(4*8, timelimit, "TL", ui.drawstring(pos, sprintf("%-3g", gethostcachenumber(field_timelimit, sv)), '8 8', col, 1, 0);)
#define COLUMN_GAMEDIR COLUMN(8*8, gamedir, "Gamedir", ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_gamedir, sv)), '8 8', col, 1, 0);)
#define COLUMN_ADDRESS COLUMN(16*8, address, "Address", ui.drawstring(pos, sprintf("%-.16s", gethostcachestring(field_address, sv)), '8 8', col, 1, 0);)
#define COLUMN_ADDRESS COLUMN(21*8, address, "Address", ui.drawstring(pos, sprintf("%-.21s", gethostcachestring(field_address, sv)), '8 8', col, 1, 0);)
#define COLUMN_MAP COLUMN(8*8, map, "Map", ui.drawstring(pos, sprintf("%-.8s", gethostcachestring(field_map, sv)), '8 8', col, 1, 0);)
#define COLUMN_HOSTNAME COLUMN(64*8, name, "Name", ui.drawstring(pos, sprintf("%s", gethostcachestring(field_name, sv)), '8 8', col, 1, 0);)
//FIXME: add a little * icon before the hostname for favourites or something
@ -149,6 +149,20 @@ class mitem_servers : mitem
ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
};
static string() getgamedircmd =
{ //sadly the vanilla NQ network protocol gave clients no idea which gamedir the server is actually using.
//many extended protocols still lack that info, so be sure to try to switch according to the server browser's gamedir info, because its more robust than not even trying.
float field_gamedir = gethostcacheindexforkey("gamedir");
if (field_gamedir < 0)
field_gamedir = gethostcacheindexforkey("mod");
if (field_gamedir < 0)
return ""; //erk. dodgy engine?
string gd = gethostcachestring(field_gamedir, server_selected);
if (strstrofs(gd, "\"")>=0 || strstrofs(gd, "\n")>=0 || strstrofs(gd, "\r")>=0)
return ""; //no, just no. trust the engine to reject bad paths, we're just preventing cbuf corruption here (DP doesn't support "%S").
return sprintf("gamedir \"%s\";", gd);
};
virtual float(vector pos, float scan, float char, float down) item_keypress =
{
float displaysize;
@ -195,7 +209,7 @@ class mitem_servers : mitem
//connect on double clicks. because we can.
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
localcmd(sprintf("m_pop;%sconnect \"%s\"\n", getgamedircmd(), addr));
}
else
server_selected = news;
@ -207,19 +221,19 @@ class mitem_servers : mitem
{ //connect normally
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;connect \"%s\"\n", addr));
localcmd(sprintf("m_pop;%sconnect \"%s\"\n", getgamedircmd(), addr));
}
else if (scan == 's')
{ //s = join as a spectator
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;observe \"%s\"\n", addr));
localcmd(sprintf("m_pop;%sobserve \"%s\"\n", getgamedircmd(), addr));
}
else if (scan == 'j')
{ //s = join as a spectator
addr = gethostcachestring(gethostcacheindexforkey("cname"), server_selected);
if (addr)
localcmd(sprintf("m_pop;join \"%s\"\n", addr));
localcmd(sprintf("m_pop;%sjoin \"%s\"\n", getgamedircmd(), addr));
}
else if (scan == K_UPARROW || scan == K_MWHEELUP)
{
@ -303,7 +317,10 @@ class mitem_servers_players : mitem
return;
for (player = 0, y = 0; player < 256; player++)
{
string playerinfo = gethostcachestring(gethostcacheindexforkey(sprintf("player%g", player)), listing.server_selected);
float key = gethostcacheindexforkey(sprintf("player%g", player));
if (key < 0)
break; //probably DP. this isn't going to work for us.
string playerinfo = gethostcachestring(key, listing.server_selected);
if (!playerinfo)
break;
tokenize(playerinfo);
@ -335,19 +352,23 @@ class mitem_servers_players : mitem
pos_x += 16*6;
y = 0;
}
// drawtextfield(opos, item_size, 3, gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected));
m = tokenizebyseparator(gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected), "\\");
for(player = 1; player <= m; player += 2)
if (checkbuiltin(drawtextfield))
{
drawtextfield(pos, '64 8', 6, argv(player));
drawtextfield(pos+'68 0', [32*8-40, 8], 3, argv(player+1));
pos_y += 8;
if (++y == 8)
// drawtextfield(opos, item_size, 3, gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected));
m = tokenizebyseparator(gethostcachestring(gethostcacheindexforkey("serverinfo"), listing.server_selected), "\\");
for(player = 1; player <= m; player += 2)
{
y-= 8;
pos_y -= 8*8;
pos_x += 32*8;
drawtextfield(pos, '64 8', 6, argv(player));
drawtextfield(pos+'68 0', [32*8-40, 8], 3, argv(player+1));
pos_y += 8;
if (++y == 8)
{
y-= 8;
pos_y -= 8*8;
pos_x += 32*8;
}
}
}
};

View File

@ -1,6 +1,6 @@
#include "../menusys/mitem_grid.qc"
enum
enum : int
{
GPMI_NAME, //name of the package, for use with the pkg command.
GPMI_CATEGORY, //category text
@ -20,6 +20,7 @@ class mitem_updategrid : mitem_grid
{
virtual void(vector pos) item_draw =
{ //make sure we see any updates as they're detected...
__using getpackagemanagerinfo, ftoi;
if (getpackagemanagerinfo(grid_numchildren, GPMI_NAME))
{
grid_numchildren++;
@ -35,6 +36,7 @@ class mitem_updategrid : mitem_grid
};
void(vector pos, float idx) mitem_updategrid::grid_draw =
{
__using getpackagemanagerinfo, ftoi;
string text = getpackagemanagerinfo(idx, GPMI_TITLE);
vector col = item_rgb;
@ -88,6 +90,7 @@ void(vector pos, float idx) mitem_updategrid::grid_draw =
};
*/float(vector pos, float scan, float char, float down, float idx) mitem_updategrid::grid_keypress =
{
__using getpackagemanagerinfo, ftoi;
string text;
if (!down)
return FALSE;
@ -136,6 +139,7 @@ class menu_updates : mitem_exmenu
virtual string(string key) get =
{
__using getpackagemanagerinfo, ftoi;
if (key == "info")
{
string text=__NULL__, tmp;
@ -157,6 +161,9 @@ void(mitem_desktop desktop) M_Menu_Updates =
mitem it;
float h = (480+240)/2;
if (!checkbuiltin(getpackagemanagerinfo))
return; //not supported in this engine...
localcmd("pkg update\n");
//create the menu, give it focus, and make sure its displayed over everything else.

View File

@ -31,6 +31,7 @@ void() mitem_desktop::mitem_desktop =
#define menu_font_fallback autocvar(gl_font, "")
queryscreensize();
__using(loadfont, drawfont)
if (checkextension("DP_GFX_FONTS"))
{
//make sure we have a font that can cope with slightly up-scaled stuff.
@ -127,8 +128,11 @@ float(vector pos, float scan, float char, float down) mitem_desktop::item_keypre
if (scan == K_MOUSE1 && down)
{
#if defined(CSQC) && defined(FTE_SPLITSCREEN)
if (numclientseats)
localcmd("in_forcesplitclient 0\n");
__using(numclientseats)
{
if (numclientseats)
cvar_set("in_forceseat", "0"); //disable it.
}
#endif
if (item_flags & IF_NOCURSOR)
{
@ -140,12 +144,15 @@ float(vector pos, float scan, float char, float down) mitem_desktop::item_keypre
if (scan == K_MOUSE2 && down)
{
#if defined(CSQC) && defined(FTE_SPLITSCREEN)
if (numclientseats > 3)
localcmd(strcat("in_forcesplitclient ", ftos(1 + ((ui.mousepos[0]>ui.screensize[0]/2)?1:0) + ((ui.mousepos[1]>ui.screensize[1]/2)?2:0)), "\n"));
else if (numclientseats > 1)
localcmd(strcat("in_forcesplitclient ", ftos(1 + floor(ui.mousepos[1]*numclientseats/ui.screensize[1])), "\n"));
else if (numclientseats)
localcmd("in_forcesplitclient 0\n");
__using(numclientseats)
{
if (numclientseats > 3)
cvar_set("in_forceseat", ftos(1 + ((ui.mousepos[0]>ui.screensize[0]/2)?1:0) + ((ui.mousepos[1]>ui.screensize[1]/2)?2:0)));
else if (numclientseats > 1)
cvar_set("in_forceseat", ftos(1 + floor(ui.mousepos[1]*numclientseats/ui.screensize[1])));
else if (numclientseats)
cvar_set("in_forceseat", "0"); //disable it.
}
#endif
if (item_flags & IF_NOCURSOR)
{
@ -185,7 +192,7 @@ void(float seat, vector minpos, vector size) mitem_desktop::drawgame_helper =
clearscene();
addentities(MASK_ENGINE|MASK_VIEWMODEL);
}
else
else __using(VF_VIEWENTITY, VF_LPLAYER)
{
setviewprop(VF_LPLAYER, seat);
setproperty(VF_VIEWENTITY, player_localentnum);
@ -220,6 +227,7 @@ void(vector pos) mitem_desktop::item_draw =
else if (this.drawgame != __NULL__)
{
#ifdef FTE_SPLITSCREEN
__using numclientseats, VF_LPLAYER;
if (numclientseats > 3)
{
drawgame_helper(0, [0, 0], 0.5*ui.screensize);

View File

@ -67,10 +67,18 @@ class mitem_spinnymodel : mitem
//it might end up clobbering any fields used for multiple things...
virtual void(vector pos) item_draw =
{
#ifndef CSQC
if (!checkbuiltin(clearscene) || !checkbuiltin(setviewprop) || !checkbuiltin(addentity) || !checkbuiltin(renderscene))
{
drawstring(pos, "Renderscene unsupported", [8,8], [1,1,1],1,0);
return;
}
__using VF_ORIGIN, VF_ANGLES, VF_MIN, VF_SIZE, VF_FOV, VF_AFOV;
#endif
vector orgbias;
orgbias = '0 0 0';
if (dp_workarounds)
orgbias = (vector)getviewprop(VF_ORIGIN); //DP still lights the entity even if the world isn't drawn. this results in inconsistant/buggy light levels. there's nothing we can do about that other than stopping it from being completely black.
__using(getviewprop) orgbias = (vector)getviewprop(VF_ORIGIN); //DP still lights the entity even if the world isn't drawn. this results in inconsistant/buggy light levels. there's nothing we can do about that other than stopping it from being completely black.
origin = orgbias;
origin_z += zbias;
@ -137,8 +145,13 @@ class mitem_spinnymodel : mitem
// return;
}
precache_model(item_text);
setmodel(this, item_text); //use the size information from the engine, woo for unreliability.
#ifndef CSQC
if (checkbuiltin(precache_model) && checkbuiltin(setmodel))
{
precache_model(item_text);
setmodel(this, item_text); //use the size information from the engine, woo for unreliability.
}
#endif
zbias += (mins_z - maxs_z)/2 - mins_z; //center the model on its z axis, so the whole thing is visible.
frame = firstframe;
if (!angles_y && !rotatespeed)

View File

@ -171,7 +171,13 @@ class mitem_label : mitem
if (ui.drawrectmax[0] > ui.drawrectmin[0] && ui.drawrectmax[1] > ui.drawrectmin[1])
{
ui.setcliparea(ui.drawrectmin[0], ui.drawrectmin[1], ui.drawrectmax[0] - ui.drawrectmin[0], ui.drawrectmax[1] - ui.drawrectmin[1]);
clientsize[1] = drawtextfield(textpos, clientsize, fl, text) * item_scale;
if (checkbuiltin(drawtextfield))
clientsize[1] = drawtextfield(textpos, clientsize, fl, text) * item_scale;
else
{
drawstring(textpos, text, [8,8], [1,1,1],1,0);
clientsize[1] = 8;
}
ui.setcliparea(omin_x, omin_y, omax_x - omin_x, omax_y - omin_y);
if (vslider)