Compare commits
4 commits
83202a6174
...
410e3fc34e
Author | SHA1 | Date | |
---|---|---|---|
410e3fc34e | |||
60128c6b1e | |||
0ea41d9304 | |||
0bf9374016 |
19 changed files with 444 additions and 201 deletions
|
@ -50,7 +50,7 @@ Colors_Init(void)
|
|||
col_refresh_text = [255,255,255] / 255;
|
||||
col_refresh_bg = [56,56,56] / 255;
|
||||
|
||||
col_button_bg = [1,1,1];
|
||||
col_button_bg = [247,181,57] / 255;
|
||||
col_button_select = [1,0,0];
|
||||
col_button_click = [1,1,0];
|
||||
col_button_disabled = [0.25, 0.25, 0.25];
|
||||
|
|
|
@ -103,44 +103,6 @@ var int gameinfo_current = -1;
|
|||
int gameinfo_count;
|
||||
gameinfo_t *games;
|
||||
|
||||
#ifndef WEBMENU
|
||||
string(float id, float b) getgamedirinfo = #0;
|
||||
string(int packageidx, int desiredfield) getpackagemanagerinfo = #0;
|
||||
|
||||
enum
|
||||
{
|
||||
GPMI_NAME, // name of the package, for use with the pkg command.
|
||||
GPMI_CATEGORY, // category text
|
||||
GPMI_TITLE, // name of the package, for showing the user.
|
||||
GPMI_VERSION, // version info (may have multiple with the same name but different versions)
|
||||
GPMI_DESCRIPTION, // some blurb
|
||||
GPMI_LICENSE, // what license its distributed under
|
||||
GPMI_AUTHOR, // name of the person(s) who created it
|
||||
GPMI_WEBSITE, // where to contribute/find out more info/etc
|
||||
GPMI_INSTALLED, // current state
|
||||
GPMI_ACTION, // desired state
|
||||
GPMI_AVAILABLE, // whether it may be downloaded or not.
|
||||
GPMI_FILESIZE, // whether it may be downloaded or not.
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string name;
|
||||
string category;
|
||||
string title;
|
||||
string version;
|
||||
string description;
|
||||
string license;
|
||||
string author;
|
||||
string website;
|
||||
string installed;
|
||||
int size;
|
||||
int uid;
|
||||
} update_t;
|
||||
int update_count;
|
||||
update_t *updates;
|
||||
#endif
|
||||
|
||||
int g_menupage;
|
||||
|
||||
enum
|
||||
|
|
|
@ -63,6 +63,22 @@ Menu_GammaHack(void)
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Menu_HasStartupVideos(void)
|
||||
{
|
||||
if (Platform_FileInCurrentGamedir("media/valve.avi"))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Menu_PlayStartupVideos(void)
|
||||
{
|
||||
print("playing startup videos\n");
|
||||
//localcmd("playvideo av:media/sierra.avi av:media/valve.avi\n");
|
||||
}
|
||||
|
||||
/* called upon menu init/restart */
|
||||
void
|
||||
m_init(void)
|
||||
|
@ -121,6 +137,7 @@ m_init(void)
|
|||
|
||||
Colors_Init();
|
||||
Strings_Init();
|
||||
Updates_Init();
|
||||
|
||||
if (games[gameinfo_current].gamedir != "valve" || games[gameinfo_current].steambg == 1) {
|
||||
m_intro_skip();
|
||||
|
@ -153,6 +170,9 @@ m_init(void)
|
|||
/* prepare spray logo keys */
|
||||
spray_setinfokeys();
|
||||
|
||||
if (Menu_HasStartupVideos() == true)
|
||||
Menu_PlayStartupVideos();
|
||||
|
||||
g_initialized = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -721,10 +721,12 @@ customgame_lbmods_changed(void)
|
|||
void
|
||||
games_setdefaultpkgs(int id)
|
||||
{
|
||||
#if 0
|
||||
if (games[id].gamedir == "valve") {
|
||||
games[id].pkgname = "valve_patch;addon_furtherdata;addon_holidaymodels";
|
||||
games[id].pkgfile = "maps/crossfire.bsp"; /* only found in patches */
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -14,27 +14,8 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#define FN_UPDATE_IMGURL "http://www.frag-net.com/dl/img/%s.jpg"
|
||||
|
||||
#ifndef WEBMENU
|
||||
enum
|
||||
{
|
||||
GPMI_NAME, // name of the package, for use with the pkg command.
|
||||
GPMI_CATEGORY, // category text
|
||||
GPMI_TITLE, // name of the package, for showing the user.
|
||||
GPMI_VERSION, // version info (may have multiple with the same name but different versions)
|
||||
GPMI_DESCRIPTION, // some blurb
|
||||
GPMI_LICENSE, // what license its distributed under
|
||||
GPMI_AUTHOR, // name of the person(s) who created it
|
||||
GPMI_WEBSITE, // where to contribute/find out more info/etc
|
||||
GPMI_INSTALLED, // current state
|
||||
GPMI_ACTION, // desired state
|
||||
GPMI_AVAILABLE, // whether it may be downloaded or not.
|
||||
GPMI_FILESIZE, // size to download.
|
||||
};
|
||||
|
||||
string(int, int) getpackagemanagerinfo = #0;
|
||||
|
||||
var int g_updates_initialized = FALSE;
|
||||
var int autocvar_menu_updating = FALSE;
|
||||
|
||||
|
@ -63,49 +44,25 @@ up_btndone_start(void)
|
|||
void
|
||||
up_btninstall_start(void)
|
||||
{
|
||||
int pkgid;
|
||||
|
||||
pkgid = up_lbUpdates.GetSelected();
|
||||
localcmd(sprintf("pkg add %s\n", updates[pkgid].name));
|
||||
updates[pkgid].installed = "pending";
|
||||
print(sprintf("Marking package %s for install.\n", updates[pkgid].title));
|
||||
Updates_Remove(up_lbUpdates.GetSelected());
|
||||
}
|
||||
|
||||
void
|
||||
up_btnremove_start(void)
|
||||
{
|
||||
int pkgid;
|
||||
|
||||
pkgid = up_lbUpdates.GetSelected();
|
||||
localcmd(sprintf("pkg rem %s\n", updates[pkgid].name));
|
||||
updates[pkgid].installed = "rem";
|
||||
print(sprintf("Marking package %s for 'removal'.\n", updates[pkgid].title));
|
||||
Updates_Install(up_lbUpdates.GetSelected());
|
||||
}
|
||||
|
||||
void
|
||||
up_toggleinstall(void)
|
||||
{
|
||||
int pkgid;
|
||||
pkgid = up_lbUpdates.GetSelected();
|
||||
|
||||
switch (updates[pkgid].installed) {
|
||||
case "":
|
||||
case "rem":
|
||||
localcmd(sprintf("pkg add %s\n", updates[pkgid].name));
|
||||
updates[pkgid].installed = "pending";
|
||||
break;
|
||||
default:
|
||||
localcmd(sprintf("pkg rem %s\n", updates[pkgid].name));
|
||||
updates[pkgid].installed = "rem";
|
||||
}
|
||||
Updates_Toggle(up_lbUpdates.GetSelected());
|
||||
}
|
||||
|
||||
void
|
||||
up_btnapply_start(void)
|
||||
{
|
||||
cvar_set("menu_updating", "1");
|
||||
localcmd("pkg apply\n");
|
||||
print("Applying package changes.\n");
|
||||
Updates_ApplyPendingChanges();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -134,94 +91,14 @@ up_sbupdates_changed(int val)
|
|||
up_lbUpdates.SetScroll(val);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
menu_updates_refresh(void)
|
||||
{
|
||||
int c;
|
||||
update_count = 0;
|
||||
Updates_Refresh();
|
||||
int updateCount = Updates_GetPackageCount();
|
||||
|
||||
if (updates) {
|
||||
memfree(updates);
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
|
||||
string cat = getpackagemanagerinfo(i, GPMI_CATEGORY);
|
||||
if (cat == "Plugins/") {
|
||||
continue;
|
||||
}
|
||||
if (cat == "Mod/") {
|
||||
continue;
|
||||
}
|
||||
update_count++;
|
||||
}
|
||||
#else
|
||||
/* look for the valid packages in the gameinfo pkgname */
|
||||
int pkgcount = tokenize(games[gameinfo_current].pkgname);
|
||||
for (int i = 0i; i < pkgcount; i++) {
|
||||
int id = game_getpackageid(argv(i));
|
||||
if (id == -1)
|
||||
continue;
|
||||
|
||||
update_count++;
|
||||
}
|
||||
#endif
|
||||
|
||||
c = 0;
|
||||
updates = memalloc(sizeof(update_t) * update_count);
|
||||
|
||||
#if 0
|
||||
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
|
||||
string cat = getpackagemanagerinfo(i, GPMI_CATEGORY);
|
||||
if (cat == "Plugins/") {
|
||||
continue;
|
||||
}
|
||||
if (cat == "Mod/") {
|
||||
continue;
|
||||
}
|
||||
|
||||
updates[c].name = getpackagemanagerinfo(i, GPMI_NAME);
|
||||
updates[c].category = getpackagemanagerinfo(i, GPMI_CATEGORY);
|
||||
updates[c].title = getpackagemanagerinfo(i, GPMI_TITLE);
|
||||
updates[c].version = getpackagemanagerinfo(i, GPMI_VERSION);
|
||||
updates[c].description = getpackagemanagerinfo(i, GPMI_DESCRIPTION);
|
||||
updates[c].license = getpackagemanagerinfo(i, GPMI_LICENSE);
|
||||
updates[c].author = getpackagemanagerinfo(i, GPMI_AUTHOR);
|
||||
updates[c].website = getpackagemanagerinfo(i, GPMI_WEBSITE);
|
||||
updates[c].installed = getpackagemanagerinfo(i, GPMI_INSTALLED);
|
||||
updates[c].size = (int)stof(getpackagemanagerinfo(i, GPMI_FILESIZE));
|
||||
updates[c].uid = i;
|
||||
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
|
||||
|
||||
c++;
|
||||
}
|
||||
#else
|
||||
for (int i = 0i; i < pkgcount; i++) {
|
||||
int id = game_getpackageid(argv(i));
|
||||
|
||||
if (id == -1)
|
||||
continue;
|
||||
|
||||
updates[c].name = getpackagemanagerinfo(id, GPMI_NAME);
|
||||
updates[c].category = getpackagemanagerinfo(id, GPMI_CATEGORY);
|
||||
updates[c].title = getpackagemanagerinfo(id, GPMI_TITLE);
|
||||
updates[c].version = getpackagemanagerinfo(id, GPMI_VERSION);
|
||||
updates[c].description = getpackagemanagerinfo(id, GPMI_DESCRIPTION);
|
||||
updates[c].license = getpackagemanagerinfo(id, GPMI_LICENSE);
|
||||
updates[c].author = getpackagemanagerinfo(id, GPMI_AUTHOR);
|
||||
updates[c].website = getpackagemanagerinfo(id, GPMI_WEBSITE);
|
||||
updates[c].installed = getpackagemanagerinfo(id, GPMI_INSTALLED);
|
||||
updates[c].size = (int)stof(getpackagemanagerinfo(id, GPMI_FILESIZE));
|
||||
updates[c].uid = id;
|
||||
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
|
||||
|
||||
c++;
|
||||
}
|
||||
#endif
|
||||
|
||||
up_sbUpdates.SetMax(update_count);
|
||||
up_lbUpdates.SetMax(update_count);
|
||||
up_sbUpdates.SetMax(updateCount);
|
||||
up_lbUpdates.SetMax(updateCount);
|
||||
up_lbUpdates.SetSelected(0);
|
||||
}
|
||||
#endif
|
||||
|
@ -281,6 +158,8 @@ menu_updates_init(void)
|
|||
up_frPreview.SetPos(350,160);
|
||||
up_frPreview.SetSize(256+6,128+6);
|
||||
Widget_Add(fn_updates, up_frPreview);
|
||||
|
||||
Updates_Refresh();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -312,8 +191,8 @@ menu_updates_draw(void)
|
|||
}
|
||||
|
||||
/* query until 1 package is ready */
|
||||
for (int i = 0; (getpackagemanagerinfo(i, GPMI_NAME)); i++) {
|
||||
string installed = getpackagemanagerinfo(i, GPMI_INSTALLED);
|
||||
for (int i = 0; (Updates_GetInfo(i, GPMI_NAME)); i++) {
|
||||
string installed = Updates_GetInfo(i, GPMI_INSTALLED);
|
||||
/* increment to keep track */
|
||||
if (installed == "enabled")
|
||||
old_enabled++;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "util.h"
|
||||
#include "achievements.h"
|
||||
#include "master.h"
|
||||
#include "modserver.h"
|
||||
|
@ -21,4 +22,4 @@
|
|||
#include "richpresence.h"
|
||||
#include "servers.h"
|
||||
#include "tcp.h"
|
||||
#include "util.h"
|
||||
#include "updates.h"
|
|
@ -7,4 +7,5 @@ richpresence.qc
|
|||
servers.qc
|
||||
tcp.qc
|
||||
util.qc
|
||||
updates.qc
|
||||
#endlist
|
||||
|
|
|
@ -33,6 +33,12 @@ float srv_fldPlayers;
|
|||
float srv_fldMaxplayers;
|
||||
float srv_fldMap;
|
||||
float srv_fldGame;
|
||||
float srv_fldServerInfo;
|
||||
float srv_fldPlayer0;
|
||||
|
||||
/* We cache these, because the engine may purge our cache anyway */
|
||||
int g_masterInternetServers;
|
||||
int g_masterLANServers;
|
||||
|
||||
/** Returns IP of master server. */
|
||||
string Master_Resolve(void);
|
||||
|
|
|
@ -45,45 +45,39 @@ Master_GetTotalServers(void)
|
|||
int
|
||||
Master_GetLANServers(void)
|
||||
{
|
||||
int count = 0;
|
||||
int tcount = 0;
|
||||
|
||||
count = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
string address;
|
||||
address = gethostcachestring(srv_fldAdress, i);
|
||||
|
||||
/* skip LAN */
|
||||
if (!address || !Server_IsLan(address)) {
|
||||
continue;
|
||||
}
|
||||
tcount++;
|
||||
}
|
||||
|
||||
return tcount;
|
||||
return g_masterLANServers;
|
||||
}
|
||||
|
||||
int
|
||||
Master_GetInternetServers(void)
|
||||
{
|
||||
int count = 0;
|
||||
int tcount = 0;
|
||||
return g_masterInternetServers;
|
||||
}
|
||||
|
||||
count = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
|
||||
void
|
||||
Master_RecountServers(void)
|
||||
{
|
||||
int count = 0i;
|
||||
g_masterInternetServers = 0i;
|
||||
g_masterInternetServers = 0i;
|
||||
|
||||
count = (int)gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
string address;
|
||||
address = gethostcachestring(srv_fldAdress, i);
|
||||
|
||||
/* skip LAN */
|
||||
if (!address || Server_IsLan(address)) {
|
||||
/* skip empty entries */
|
||||
if not (address)
|
||||
continue;
|
||||
}
|
||||
tcount++;
|
||||
}
|
||||
|
||||
return tcount;
|
||||
/* skip LAN */
|
||||
if (Server_IsLan(address)) {
|
||||
g_masterLANServers++;
|
||||
} else {
|
||||
g_masterInternetServers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -95,7 +89,8 @@ Master_RefreshCache(void)
|
|||
sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
|
||||
refreshhostcache(FALSE);
|
||||
resorthostcache();
|
||||
int a = gethostcachevalue(SLIST_HOSTCACHETOTALCOUNT);
|
||||
Master_RecountServers();
|
||||
int a = Master_GetLANServers() + Master_GetInternetServers();
|
||||
|
||||
if (a) {
|
||||
NSLog("Master reports a total of %i servers.", a);
|
||||
|
@ -111,7 +106,8 @@ Master_UpdateCache(void)
|
|||
sethostcachesort(gethostcacheindexforkey("ping"), FALSE);
|
||||
refreshhostcache(TRUE);
|
||||
resorthostcache();
|
||||
int a = gethostcachevalue(SLIST_HOSTCACHETOTALCOUNT);
|
||||
Master_RecountServers();
|
||||
int a = Master_GetLANServers() + Master_GetInternetServers();
|
||||
|
||||
if (a) {
|
||||
NSLog("Master reports a total of %i servers.", a);
|
||||
|
@ -129,6 +125,8 @@ Master_ResortCache(void)
|
|||
srv_fldMaxplayers = gethostcacheindexforkey("maxplayers");
|
||||
srv_fldMap = gethostcacheindexforkey("map");
|
||||
srv_fldGame = gethostcacheindexforkey("game");
|
||||
srv_fldServerInfo = gethostcacheindexforkey("serverinfo");
|
||||
srv_fldPlayer0 = gethostcacheindexforkey("player");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
67
src/platform/updates.h
Normal file
67
src/platform/updates.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2016-2022 Vera Visions LLC.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
string(float id, float b) getgamedirinfo = #0;
|
||||
string(int packageidx, int desiredfield) getpackagemanagerinfo = #0;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GPMI_NAME, /**< name of the package, for use with the pkg command. */
|
||||
GPMI_CATEGORY, /**< category text */
|
||||
GPMI_TITLE, /**< name of the package, for showing the user. */
|
||||
GPMI_VERSION, /**< version info (may have multiple with the same name but different versions) */
|
||||
GPMI_DESCRIPTION, /**< some blurb */
|
||||
GPMI_LICENSE, /**< what license its distributed under */
|
||||
GPMI_AUTHOR, /**< name of the person(s) who created it */
|
||||
GPMI_WEBSITE, /**< where to contribute/find out more info/etc */
|
||||
GPMI_INSTALLED, /**< current state */
|
||||
GPMI_ACTION, /**< desired state */
|
||||
GPMI_AVAILABLE, /**< whether it may be downloaded or not. */
|
||||
GPMI_FILESIZE, /**< size to download. */
|
||||
} updateType_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string name;
|
||||
string category;
|
||||
string title;
|
||||
string version;
|
||||
string description;
|
||||
string license;
|
||||
string author;
|
||||
string website;
|
||||
string installed;
|
||||
int size;
|
||||
int uid;
|
||||
} update_t;
|
||||
|
||||
int g_platform_update_count;
|
||||
update_t *updates;
|
||||
|
||||
#define FN_UPDATE_IMGURL "http://www.frag-net.com/dl/img/%s.jpg"
|
||||
|
||||
void Updates_Init(void);
|
||||
void Updates_Refresh(void);
|
||||
int Updates_GetPackageCount(void);
|
||||
int Updates_IDForName(string);
|
||||
string Updates_NameForID(int);
|
||||
string Updates_GetInfo(int, updateType_t);
|
||||
bool Updates_Available(void);
|
||||
|
||||
bool Updates_Toggle(int);
|
||||
bool Updates_Install(int);
|
||||
bool Updates_Remove(int);
|
||||
bool Updates_ApplyPendingChanges(void);
|
153
src/platform/updates.qc
Normal file
153
src/platform/updates.qc
Normal file
|
@ -0,0 +1,153 @@
|
|||
/** needs to be called upon menu-init, and call Updates_Refresh() if auto-updates
|
||||
are enabled. if a chooser does not want updates, then we won't. */
|
||||
void
|
||||
Updates_Init(void)
|
||||
{
|
||||
/*localcmd("pkg addsource https://www.frag-net.com/pkgs/list\n");*/
|
||||
print("Update system initialized.\n");
|
||||
}
|
||||
|
||||
/** will return a cached value */
|
||||
int
|
||||
Updates_GetPackageCount(void)
|
||||
{
|
||||
return g_platform_update_count;
|
||||
}
|
||||
|
||||
/** called whenever we need to re-initialize the updates struct */
|
||||
void
|
||||
Updates_Refresh(void)
|
||||
{
|
||||
int c = 0i;
|
||||
g_platform_update_count = 0i;
|
||||
|
||||
/* clear */
|
||||
if (updates) {
|
||||
memfree(updates);
|
||||
}
|
||||
|
||||
/* count all updates that we've got in our package sources */
|
||||
for (int i = 0i; (Updates_GetInfo(i, GPMI_NAME)); i++) {
|
||||
g_platform_update_count++;
|
||||
}
|
||||
|
||||
updates = memalloc(sizeof(update_t) * g_platform_update_count);
|
||||
|
||||
/* limit it to packages that the game wants */
|
||||
tokenizebyseparator(games[gameinfo_current].pkgname, ";");
|
||||
|
||||
/* fill in all the package values */
|
||||
for (int i = 0i; i < g_platform_update_count; i++) {
|
||||
int id = game_getpackageid(argv(i));
|
||||
|
||||
if (id == -1)
|
||||
continue;
|
||||
|
||||
updates[c].name = Updates_GetInfo(id, GPMI_NAME);
|
||||
updates[c].category = Updates_GetInfo(id, GPMI_CATEGORY);
|
||||
updates[c].title = Updates_GetInfo(id, GPMI_TITLE);
|
||||
updates[c].version = Updates_GetInfo(id, GPMI_VERSION);
|
||||
updates[c].description = Updates_GetInfo(id, GPMI_DESCRIPTION);
|
||||
updates[c].license = Updates_GetInfo(id, GPMI_LICENSE);
|
||||
updates[c].author = Updates_GetInfo(id, GPMI_AUTHOR);
|
||||
updates[c].website = Updates_GetInfo(id, GPMI_WEBSITE);
|
||||
updates[c].installed = Updates_GetInfo(id, GPMI_INSTALLED);
|
||||
updates[c].size = (int)stof(Updates_GetInfo(id, GPMI_FILESIZE));
|
||||
updates[c].uid = id;
|
||||
precache_pic(sprintf(FN_UPDATE_IMGURL, updates[c].name));
|
||||
|
||||
c++;
|
||||
}
|
||||
|
||||
print(sprintf("Updates packages for this game: %i (%i Total)\n", c, g_platform_update_count));
|
||||
g_platform_update_count = c;
|
||||
}
|
||||
|
||||
/** Returns the package ID for a given name. Will return -1 when not available. */
|
||||
int
|
||||
Updates_IDForName(string packageName)
|
||||
{
|
||||
string tempString = "";
|
||||
|
||||
for (int i = 0i; (tempString = getpackagemanagerinfo(i, GPMI_NAME)); i++) {
|
||||
if (tempString == packageName)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1i;
|
||||
}
|
||||
|
||||
/** Returns the package name for a given ID. Returns __NULL__ when not available. */
|
||||
string
|
||||
Updates_NameForID(int packageID)
|
||||
{
|
||||
string packageName = getpackagemanagerinfo(packageID, GPMI_NAME);
|
||||
|
||||
if not (packageName)
|
||||
return __NULL__;
|
||||
|
||||
return packageName;
|
||||
}
|
||||
|
||||
/** Query a package (by ID) for its various info fields. See updateType_t for available fields. */
|
||||
string
|
||||
Updates_GetInfo(int packageID, updateType_t fieldType)
|
||||
{
|
||||
return getpackagemanagerinfo(packageID, (int)fieldType);
|
||||
}
|
||||
|
||||
/** Returns if our current game has updates available for any installed packages. */
|
||||
bool
|
||||
Updates_Available(void)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Toggle the installation of a package. Will return true if it was done. */
|
||||
bool
|
||||
Updates_Toggle(int packageID)
|
||||
{
|
||||
switch (updates[packageID].installed) {
|
||||
case "":
|
||||
case "rem":
|
||||
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
|
||||
updates[packageID].installed = "pending";
|
||||
break;
|
||||
default:
|
||||
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
|
||||
updates[packageID].installed = "rem";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Mark a package as pending installion. May return true/false if it succeeded in marking the package. */
|
||||
bool
|
||||
Updates_Install(int packageID)
|
||||
{
|
||||
localcmd(sprintf("pkg add %s\n", updates[packageID].name));
|
||||
updates[packageID].installed = "pending";
|
||||
print(sprintf("Marking package %s for install.\n", updates[packageID].title));
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Mark a package as pending deletion. May return true/false if it succeeded in marking the package. */
|
||||
bool
|
||||
Updates_Remove(int packageID)
|
||||
{
|
||||
|
||||
localcmd(sprintf("pkg rem %s\n", updates[packageID].name));
|
||||
updates[packageID].installed = "rem";
|
||||
print(sprintf("Marking package %s for 'removal'.\n", updates[packageID].title));
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Apply all pending changes to packages. May return true/false if it succeeded in doing so. */
|
||||
bool
|
||||
Updates_ApplyPendingChanges(void)
|
||||
{
|
||||
cvar_set("menu_updating", "1");
|
||||
localcmd("pkg apply\n");
|
||||
print("Applying package changes.\n");
|
||||
return true;
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "route.h"
|
||||
#include "way.h"
|
||||
#include "lament.h"
|
||||
|
||||
/* helper macros */
|
||||
#define EVALUATE_FIELD(fieldname, changedflag) {\
|
||||
|
|
|
@ -255,6 +255,8 @@ SV_RunClientCommand(void)
|
|||
if (self.classname != "player" && self.classname != "spectator")
|
||||
return;
|
||||
|
||||
CheatersLament((NSClientPlayer)cl, input_angles, input_buttons, input_timelength);
|
||||
|
||||
if (!Plugin_RunClientCommand()) {
|
||||
/* TODO */
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ NSGameRules.qc
|
|||
client.qc
|
||||
NSTraceAttack.qc
|
||||
vote.qc
|
||||
lament.qc
|
||||
weapons.qc
|
||||
modelevent.qc
|
||||
mapcycle.qc
|
||||
|
|
17
src/server/lament.h
Normal file
17
src/server/lament.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Vera Visions LLC.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
void CheatersLament(NSClientPlayer, vector, float, float);
|
117
src/server/lament.qc
Normal file
117
src/server/lament.qc
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Vera Visions LLC.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* cheaters lament
|
||||
|
||||
a system designed to analyze movement deltas and warn about suspicious
|
||||
behaviour as it is happening.
|
||||
|
||||
explanation:
|
||||
|
||||
intense movement comes with decreased accuracy. if a player is
|
||||
showcasing signs of above average reaction time and consistent success
|
||||
in trailing (and shooting) valid targets we are going to inform the
|
||||
server admin of suspicious behaviour.
|
||||
|
||||
the system does not act on its own, it merely gives feedback and
|
||||
expects the host to act upon it.
|
||||
|
||||
we can tally the feedback and create a score based on suspicious
|
||||
behaviour as a result. these results should ideally be made public
|
||||
to the other players so they themselves can make a choice
|
||||
|
||||
technical deep dive:
|
||||
|
||||
we accumulate the camera angle deltas over time to track
|
||||
the average mouse movement intensity over a short period of time.
|
||||
waiting a full second will actually remove a full 360 degrees
|
||||
of delta.
|
||||
then we also accumulate accuracy information, we increases a counter
|
||||
whenever the player is actively firing, while also aiming at an opposing
|
||||
player within an radius of less than 10 degrees (to compensate for rocket shots
|
||||
and othe projectile trails).
|
||||
whenever the player is firing and not aiming with that level of precision,
|
||||
the counter goes down.
|
||||
|
||||
if the deltas together reach a high point together, that must mean that
|
||||
the player is having frantic mouse movements, combined with
|
||||
incredible hit accuracy.
|
||||
|
||||
the high point calibration is taken from analyzing various demos
|
||||
of known pro players as well as some cheating incidents.
|
||||
|
||||
limitations:
|
||||
|
||||
when going through teleporters, the angle deltas may be high
|
||||
enough to trigger false positives.
|
||||
|
||||
this is obviously not a perfect system, but it is to be used
|
||||
as a tool to assist in the decision making that goes into
|
||||
moderating a game server.
|
||||
*/
|
||||
|
||||
void
|
||||
CheatersLament(NSClientPlayer playerEntity, vector absAngles, float buttons, float timeLength)
|
||||
{
|
||||
vector angleDelta = playerEntity.pb_last_angles - absAngles; /* diff between old and new */
|
||||
|
||||
/* only decrement above 0 */
|
||||
if (playerEntity.pb_angle_delta > 0.0)
|
||||
playerEntity.pb_angle_delta -= timeLength * 360;
|
||||
else
|
||||
playerEntity.pb_angle_delta = 0.0f;
|
||||
|
||||
/* ditto */
|
||||
if (playerEntity.pb_player_delta > 0.0)
|
||||
playerEntity.pb_player_delta -= timeLength;
|
||||
else
|
||||
playerEntity.pb_player_delta = 0.0f;
|
||||
|
||||
/* if the player is firing, calculate the player delta */
|
||||
if (buttons & INPUT_BUTTON0 || buttons & INPUT_BUTTON3) { /* primary & secondary fire counts */
|
||||
vector deltaPosition;
|
||||
float deltaDegrees;
|
||||
|
||||
makevectors( absAngles );
|
||||
for (entity e = world; (e = find(e, ::classname, "player"));) {
|
||||
deltaPosition = normalize( e.origin - playerEntity.origin );
|
||||
deltaDegrees = deltaPosition * v_forward;
|
||||
|
||||
other = world;
|
||||
traceline(e.origin, playerEntity.origin, MOVE_OTHERONLY, playerEntity);
|
||||
|
||||
if (trace_fraction == 1.0f && deltaDegrees >= 0.95) {
|
||||
playerEntity.pb_player_delta += timeLength * 4.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playerEntity.pb_last_angles = absAngles; /* set so we don't accumulate */
|
||||
|
||||
/* if our angle delta is less than 256, don't bother reporting */
|
||||
if (vlen(angleDelta) < 256.0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* we will add the difference only if it is big enough from last frame */
|
||||
playerEntity.pb_angle_delta += vlen(angleDelta);
|
||||
|
||||
/* if our delta is consistently above 1024 and we've been trailing a player for 2
|
||||
whole second... suspect foul play */
|
||||
if (playerEntity.pb_angle_delta > 1024.0 && playerEntity.pb_player_delta > 2.0) {
|
||||
localcmd(sprintf("echo Lamenting %S (%s) with (a: %f p: %f)\n", playerEntity.netname, infokey(playerEntity, INFOKEY_P_IP), playerEntity.pb_angle_delta, playerEntity.pb_player_delta));
|
||||
}
|
||||
}
|
|
@ -168,6 +168,10 @@ private:
|
|||
float m_flPainTime;
|
||||
|
||||
entity last_used;
|
||||
|
||||
float pb_angle_delta;
|
||||
float pb_player_delta;
|
||||
vector pb_last_angles;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ typedef struct
|
|||
string samples; /**< Separated list of samples. */
|
||||
string name; /**< Name of the soundDef. */
|
||||
string distshader; /**< soundDef to play where this soundDef is not audible. */
|
||||
float pointparticle;
|
||||
} snd_t;
|
||||
|
||||
/** A sound sample of a sentences.txt word sequence. */
|
||||
|
|
|
@ -172,6 +172,9 @@ Sound_ParseField(int i, int a)
|
|||
case "distshader":
|
||||
g_sounds[i].distshader = argv(1);
|
||||
break;
|
||||
case "pointparticle":
|
||||
g_sounds[i].pointparticle = particleeffectnum(argv(1));
|
||||
break;
|
||||
case "alerts":
|
||||
dprint("\tSound set to alert enemy AI\n");
|
||||
g_sounds[i].flags |= SNDFL_ALERTS;
|
||||
|
@ -550,6 +553,14 @@ Sound_Play(entity target, int chan, string shader)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CLIENT
|
||||
if (g_sounds[sample].pointparticle)
|
||||
pointparticles(g_sounds[sample].pointparticle, target.origin, [0,0,0], 1);
|
||||
#else
|
||||
if (g_sounds[sample].pointparticle)
|
||||
print(sprintf("SoundDef %S is attempting to spawn a particle on the server side.\n", shader));
|
||||
#endif
|
||||
|
||||
sound(
|
||||
target,
|
||||
chan,
|
||||
|
|
Loading…
Reference in a new issue