Merge pull request #592 from XLabsProject/develop

Release v2.1.1
This commit is contained in:
Maurice Heumann 2023-01-25 08:51:10 +01:00 committed by GitHub
commit 86ee38de19
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 4582 additions and 290 deletions

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Discord Server
url: https://discord.gg/sKeVmR3/
about: Please ask and answer support questions here.
- name: XLabsProject Website
url: https://xlabs.dev/
about: The official website.

29
.github/ISSUE_TEMPLATE/get-help.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
name: Get help
about: Get help using S1x
title: ''
labels: discussion
assignees: ''
---
_Do not open an issue here if you need help with modding or have a problem getting the client to run.
It is very likely your problem will be resolved by reading the [FAQ](https://xlabs.dev/s1x_faq) carefully.
Ask in `s1x-support` or `s1x-modding` channels on the [Discord](https://discord.gg/sKeVmR3) server if you still have problems.
Before opening a new issue, please see [Issues](https://github.com/XLabsProject/s1x-client/issues) and check that a similar issue does not already exist
If this does not apply, please continue by filling in the template below._
**What are you trying to do?**
A short, concise description of the outcome you are trying to achieve.
**What problem are you having?**
A clear and concise description of the problem that is blocking you from your desired outcome, ex. "S1x is crashing with this error message: ..."
If S1x is crashing, include the minidump file and the crash address in text form. Screenshots showing the message box with the crash address are not acceptable.
**What version of S1x are you using?**
Please make sure you are up to date with the latest build from the master branch.
You should be using the official XLabs Launcher.
You should *not* be using any custom builds of the game made for "trickshotting"
**Anything else we should know?**
Add any other context about the problem here.

View File

@ -0,0 +1,27 @@
---
name: Request a feature
about: Suggest a new feature or enhancement
title: ''
labels: feature
assignees: ''
---
_Before opening a new feature request, please see [Issues](https://github.com/XLabsProject/s1x-client/issues) and check that a similar issue does not already exist.
If this a new request, help us help you by filling in the template below.
S1x is not in active development right now. Please keep in mind that if there is not a clear positive impact on the gameplay to be gained by this feature request, it may be closed at the maintainers' discretion._
**What problem will this solve?**
A clear and concise description of the problem this new feature is meant to solve, ex. "There is something wrong with the game" or "There is something wrong with S1x's source code".
Please limit your request to a single feature; create multiple feature requests instead.
**What might be a solution?**
A clear and concise description of what you want to happen. If you are proposing changes to S1x's source code, tell us which component you want to be changed.
If you propose changes to the game, you may use images or videos to illustrate what aspect of the game should be changed.
**What other alternatives have you already considered?**
A clear and concise description of any alternative solutions or features you've considered.
It may help others to find workarounds until the problem is resolved.
**Anything else we should know?**
Add any other context or screenshots about the feature request here.

View File

@ -8,6 +8,11 @@ on:
branches:
- "*"
types: [opened, synchronize, reopened]
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Build binaries
@ -18,14 +23,6 @@ jobs:
- Debug
- Release
steps:
- name: Wait for previous workflows
if: github.event_name == 'push' && github.ref == 'refs/heads/master'
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Check out files
uses: actions/checkout@v3
with:
@ -95,13 +92,6 @@ jobs:
- name: Add known hosts
run: ssh-keyscan -H ${{ secrets.XLABS_MASTER_SSH_ADDRESS }} >> ~/.ssh/known_hosts
- name: Wait for previous workflows
uses: softprops/turnstyle@v1
with:
poll-interval-seconds: 10
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Remove old data files
run: ssh ${{ secrets.XLABS_MASTER_SSH_USER }}@${{ secrets.XLABS_MASTER_SSH_ADDRESS }} rm -rf ${{ env.XLABS_MASTER_PATH }}/s1x/data/*

File diff suppressed because it is too large Load Diff

View File

@ -1211,10 +1211,6 @@ removeplayerondisconnect()
initclientdvarssplitscreenspecific()
{
if ( level.splitscreen || self issplitscreenplayer() )
self setclientdvars( "cg_hudGrenadeIconHeight", "37.5", "cg_hudGrenadeIconWidth", "37.5", "cg_hudGrenadeIconOffset", "75", "cg_hudGrenadePointerHeight", "18", "cg_hudGrenadePointerWidth", "37.5", "cg_hudGrenadePointerPivot", "18 40.5", "cg_fovscale", "0.75" );
else
self setclientdvars( "cg_hudGrenadeIconHeight", "75", "cg_hudGrenadeIconWidth", "75", "cg_hudGrenadeIconOffset", "50", "cg_hudGrenadePointerHeight", "36", "cg_hudGrenadePointerWidth", "75", "cg_hudGrenadePointerPivot", "36 81", "cg_fovscale", "1" );
}
initclientdvars()
@ -1236,24 +1232,6 @@ initclientdvars()
setdvar( "cg_drawFriendlyNamesAlways", 1 );
else
setdvar( "cg_drawFriendlyNamesAlways", 0 );
initclientdvarssplitscreenspecific();
if ( maps\mp\_utility::getgametypenumlives() )
self setclientdvars( "cg_deadChatWithDead", 1, "cg_deadChatWithTeam", 0, "cg_deadHearTeamLiving", 0, "cg_deadHearAllLiving", 0 );
else
self setclientdvars( "cg_deadChatWithDead", 0, "cg_deadChatWithTeam", 1, "cg_deadHearTeamLiving", 1, "cg_deadHearAllLiving", 0 );
if ( level.teambased )
self setclientdvars( "cg_everyonehearseveryone", 0 );
if ( getdvarint( "scr_hitloc_debug" ) )
{
for ( var_0 = 0; var_0 < 6; var_0++ )
self setclientdvar( "ui_hitloc_" + var_0, "" );
self.hitlocinited = 1;
}
}
getlowestavailableclientid()
@ -1573,7 +1551,7 @@ callback_playerconnect()
thread kickifdontspawn();
return;
}
else if ( !maps\mp\_utility::matchmakinggame() && maps\mp\_utility::allowteamchoice() )
else if ( maps\mp\_utility::allowteamchoice() && !isbot( self ) )
{
maps\mp\gametypes\_menus::menuspectator();
thread setuioptionsmenu( 1 );

View File

@ -0,0 +1,517 @@
// S1 GSC SOURCE
// Decompiled by https://github.com/xensik/gsc-tool
main()
{
maps\mp\gametypes\_globallogic::init();
maps\mp\gametypes\_callbacksetup::setupcallbacks();
maps\mp\gametypes\_globallogic::setupcallbacks();
setguns();
maps\mp\_utility::registertimelimitdvar( level.gametype, 10 );
setDvar( "scr_gun_scorelimit", level.gun_guns.size );
maps\mp\_utility::registerscorelimitdvar( level.gametype, level.gun_guns.size );
level thread reinitializescorelimitonmigration();
maps\mp\_utility::registerroundlimitdvar( level.gametype, 1 );
maps\mp\_utility::registerwinlimitdvar( level.gametype, 0 );
maps\mp\_utility::registernumlivesdvar( level.gametype, 0 );
maps\mp\_utility::registerhalftimedvar( level.gametype, 0 );
level.matchrules_randomize = 0;
level.matchrules_damagemultiplier = 0;
level.matchrules_vampirism = 0;
setspecialloadout();
level.teambased = 0;
level.doprematch = 1;
level.onstartgametype = ::onstartgametype;
level.onspawnplayer = ::onspawnplayer;
level.getspawnpoint = ::getspawnpoint;
level.onplayerkilled = ::onplayerkilled;
level.ontimelimit = ::ontimelimit;
level.onplayerscore = ::onplayerscore;
level.bypassclasschoicefunc = ::gungameclass;
level.assists_disabled = 1;
level.setbacklevel = maps\mp\_utility::getintproperty( "scr_setback_levels", 1 );
level.lastguntimevo = 0;
if ( level.matchrules_damagemultiplier )
level.modifyplayerdamage = maps\mp\gametypes\_damage::gamemodemodifyplayerdamage;
setteamscore( "ffa" );
game["dialog"]["gametype"] = "gg_intro";
game["dialog"]["defense_obj"] = "gbl_start";
game["dialog"]["offense_obj"] = "gbl_start";
game["dialog"]["humiliation"] = "gg_humiliation";
game["dialog"]["lastgun"] = "at_anr1_gg_lastgun";
if ( maps\mp\_utility::isgrapplinghookgamemode() )
game["dialog"]["gametype"] = "grap_" + game["dialog"]["gametype"];
}
initializematchrules()
{
maps\mp\_utility::setcommonrulesfrommatchrulesdata( 1 );
level.matchrules_randomize = getmatchrulesdata( "gunData", "randomize" );
setDvar( "scr_gun_scorelimit", level.gun_guns.size );
maps\mp\_utility::registerscorelimitdvar( level.gametype, level.gun_guns.size );
setDvar( "scr_gun_winlimit", 1 );
maps\mp\_utility::registerwinlimitdvar( "gun", 1 );
setDvar( "scr_gun_roundlimit", 1 );
maps\mp\_utility::registerroundlimitdvar( "gun", 1 );
setDvar( "scr_gun_halftime", 0 );
maps\mp\_utility::registerhalftimedvar( "gun", 0 );
setDvar( "scr_gun_playerrespawndelay", 0 );
setDvar( "scr_gun_waverespawndelay", 0 );
setDvar( "scr_player_forcerespawn", 1 );
setDvar( "scr_setback_levels", getmatchrulesdata( "gunData", "setbackLevels" ) );
}
reinitializescorelimitonmigration()
{
setDvar( "scr_gun_scorelimit", level.gun_guns.size );
maps\mp\_utility::registerscorelimitdvar( level.gametype, level.gun_guns.size );
}
onstartgametype()
{
getteamplayersalive( "auto_change" );
maps\mp\_utility::setobjectivetext( "allies", &"OBJECTIVES_DM" );
maps\mp\_utility::setobjectivetext( "axis", &"OBJECTIVES_DM" );
maps\mp\_utility::setobjectivescoretext( "allies", &"OBJECTIVES_DM_SCORE" );
maps\mp\_utility::setobjectivescoretext( "axis", &"OBJECTIVES_DM_SCORE" );
maps\mp\_utility::setobjectivehinttext( "allies", &"OBJECTIVES_DM_HINT" );
maps\mp\_utility::setobjectivehinttext( "axis", &"OBJECTIVES_DM_HINT" );
initspawns();
allowed = [];
maps\mp\gametypes\_gameobjects::main( allowed );
level.quickmessagetoall = 1;
level.blockweapondrops = 1;
level thread onplayerconnect();
}
initspawns()
{
level.spawnmins = ( 0.0, 0.0, 0.0 );
level.spawnmaxs = ( 0.0, 0.0, 0.0 );
level.spawn_name = "mp_dm_spawn";
maps\mp\gametypes\_spawnlogic::addspawnpoints( "allies", level.spawn_name );
maps\mp\gametypes\_spawnlogic::addspawnpoints( "axis", level.spawn_name );
level.mapcenter = maps\mp\gametypes\_spawnlogic::findboxcenter( level.spawnmins, level.spawnmaxs );
setmapcenter( level.mapcenter );
}
onplayerconnect()
{
for (;;)
{
level waittill( "connected", player );
player.gungamegunindex = 0;
player.gungameprevgunindex = 0;
player.stabs = 0;
player.mysetbacks = 0;
player.lastleveluptime = 0;
player.showsetbacksplash = 0;
if ( level.matchrules_randomize )
player.gunlist = common_scripts\utility::array_randomize( level.gun_guns );
player thread refillammo();
player thread refillsinglecountammo();
player thread watchforhostmigration();
}
}
getspawnpoint()
{
var_0 = maps\mp\gametypes\_spawnlogic::getteamspawnpoints( self.pers["team"] );
if ( level.ingraceperiod )
var_1 = maps\mp\gametypes\_spawnlogic::getspawnpoint_random( var_0 );
else
var_1 = maps\mp\gametypes\_spawnscoring::getspawnpoint_freeforall( var_0 );
maps\mp\gametypes\_spawnlogic::recon_set_spawnpoint( var_1 );
return var_1;
}
gungameclass()
{
self.pers["class"] = "gamemode";
self.pers["lastClass"] = "";
self.pers["gamemodeLoadout"] = level.gun_loadout;
self.class = self.pers["class"];
self.lastclass = self.pers["lastClass"];
self loadweapons( level.gun_guns[0] );
}
onspawnplayer()
{
thread waitloadoutdone();
}
waitloadoutdone()
{
level endon( "game_ended" );
self endon( "disconnect" );
level waittill( "player_spawned" );
givenextgun( 1 );
if ( self.showsetbacksplash )
{
self.showsetbacksplash = 0;
thread maps\mp\_events::decreasegunlevelevent();
}
}
watchforhostmigration()
{
level endon( "game_ended" );
self endon( "disconnect" );
for (;;)
{
self waittill( "player_migrated" );
if ( self.sessionstate == "spectator" )
maps\mp\gametypes\_menus::handleclasschoicedisallowed();
}
}
onplayerscore( var_0, var_1, var_2 )
{
if ( var_0 == "gained_gun_score" )
{
var_3 = maps\mp\gametypes\_rank::getscoreinfovalue( var_0 );
var_1 maps\mp\_utility::setextrascore0( var_1.extrascore0 + var_3 );
var_1 maps\mp\gametypes\_gamescore::updatescorestatsffa( var_1, var_3 );
return 1;
}
if ( var_0 == "dropped_gun_score" )
{
var_4 = min( level.setbacklevel, self.score );
return int( var_4 * -1 );
}
return 0;
}
onplayerkilled( var_0, attacker, var_2, sMeansOfDeath, sWeapon, var_5, var_6, var_7, var_8, var_9 )
{
if ( !isdefined( attacker ) )
return;
if ( sMeansOfDeath == "MOD_TRIGGER_HURT" && !isplayer( attacker ) )
attacker = self;
if ( sMeansOfDeath == "MOD_FALLING" || isplayer( attacker ) )
{
if ( sMeansOfDeath == "MOD_FALLING" || attacker == self || maps\mp\_utility::ismeleemod( sMeansOfDeath ) && sWeapon != "riotshield_mp" || sWeapon == "boost_slam_mp" || sWeapon == "iw5_dlcgun12loot8_mp" )
{
self playlocalsound( "mp_war_objective_lost" );
self.gungameprevgunindex = self.gungamegunindex;
self.gungamegunindex = int( max( 0, self.gungamegunindex - level.setbacklevel ) );
self.lastkillweapon = undefined;
if ( self.gungameprevgunindex > self.gungamegunindex )
{
self.mysetbacks++;
maps\mp\_utility::setextrascore1( self.mysetbacks );
self.showsetbacksplash = 1;
if ( maps\mp\_utility::ismeleemod( sMeansOfDeath ) || sWeapon == "boost_slam_mp" || sWeapon == "iw5_dlcgun12loot8_mp" )
{
attacker.stabs++;
attacker.assists = attacker.stabs;
attacker thread maps\mp\_events::setbackenemygunlevelevent();
if ( self.gungameprevgunindex == level.gun_guns.size - 1 )
{
attacker thread maps\mp\_events::setbackfirstplayergunlevelevent();
attacker maps\mp\_utility::leaderdialogonplayer( "humiliation", "status" );
}
}
}
}
else if ( sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET" || sMeansOfDeath == "MOD_HEAD_SHOT" || sMeansOfDeath == "MOD_PROJECTILE" || sMeansOfDeath == "MOD_PROJECTILE_SPLASH" || sMeansOfDeath == "MOD_EXPLOSIVE" || sMeansOfDeath == "MOD_IMPACT" || sMeansOfDeath == "MOD_GRENADE" || sMeansOfDeath == "MOD_GRENADE_SPLASH" || maps\mp\_utility::ismeleemod( sMeansOfDeath ) && sWeapon == "riotshield_mp" )
{
if ( isdefined( attacker.lastkillweapon ) && attacker.lastkillweapon == sWeapon )
{
return;
}
var_10 = level.gun_guns;
if ( level.matchrules_randomize )
var_10 = attacker.gunlist;
var_11 = var_10[attacker.gungamegunindex];
if ( !issubstr( sWeapon, maps\mp\_utility::getbaseweaponname( var_11 ) ) )
{
return;
}
attacker.lastkillweapon = sWeapon;
if ( attacker.lastleveluptime + 3000 > gettime() )
attacker thread maps\mp\_events::quickgunlevelevent();
attacker.lastleveluptime = gettime();
attacker.gungameprevgunindex = attacker.gungamegunindex;
attacker.gungamegunindex++;
attacker thread maps\mp\_events::increasegunlevelevent();
if ( attacker.gungamegunindex == level.gun_guns.size - 1 )
{
maps\mp\_utility::playsoundonplayers( "mp_enemy_obj_captured" );
level thread maps\mp\_utility::teamplayercardsplash( "callout_top_gun_rank", attacker );
var_12 = gettime();
if ( level.lastguntimevo + 4500 < var_12 )
{
level thread maps\mp\_utility::leaderdialogonplayers( "lastgun", level.players, "status" );
level.lastguntimevo = var_12;
}
}
if ( attacker.gungamegunindex < level.gun_guns.size )
attacker givenextgun( 0, sWeapon );
}
}
}
givenextgun( var_0, var_1 )
{
self endon( "disconnect" );
var_2 = getnextgun();
self.gun_curgun = var_2;
var_2 = addattachments( var_2 );
while ( !self loadweapons( var_2 ) )
{
waitframe();
}
if ( isdefined( var_1 ) )
self takeweapon( var_1 );
else
self takeallweapons();
maps\mp\_utility::_giveweapon( var_2 );
self switchtoweaponimmediate( var_2 );
if ( isdefined( var_0 ) && var_0 == 1 )
self setspawnweapon( var_2 );
var_3 = maps\mp\_utility::getbaseweaponname( var_2 );
self.pers["primaryWeapon"] = var_3;
self.primaryweapon = var_2;
self givestartammo( var_2 );
self switchtoweapon( var_2 );
self.gungameprevgunindex = self.gungamegunindex;
}
getnextgun()
{
var_0 = level.gun_guns;
var_1 = [];
newWeapon = undefined;
if ( level.matchrules_randomize )
var_0 = self.gunlist;
newWeapon = var_0[self.gungamegunindex];
var_1[var_1.size] = newWeapon;
if ( self.gungamegunindex + 1 < var_0.size )
var_1[var_1.size] = var_0[self.gungamegunindex + 1];
if ( self.gungamegunindex > 0 )
var_1[var_1.size] = var_0[self.gungamegunindex - 1];
self loadweapons( var_1 );
return newWeapon;
}
addattachments( weaponName )
{
if ( getdvarint( "scr_gun_loot_variants", 0 ) == 1 )
{
var_1 = tablelookup( "mp/statstable.csv", 4, weaponName , 40 );
if ( isdefined( var_1 ) && var_1 != "" )
var_2 = maps\mp\gametypes\_class::buildweaponname( weaponName , var_1, "none", "none", 0, 0 );
else
var_2 = maps\mp\gametypes\_class::buildweaponname( weaponName , "none", "none", "none", 0, 0 );
}
else
var_2 = maps\mp\gametypes\_class::buildweaponname( weaponName , "none", "none", "none", 0, 0 );
return var_2;
}
ontimelimit()
{
level.finalkillcam_winner = "none";
winners = gethighestprogressedplayers();
if ( !isdefined( winners ) || !winners.size )
thread maps\mp\gametypes\_gamelogic::endgame( "tie", game["end_reason"]["time_limit_reached"] );
else if ( winners.size == 1 )
thread maps\mp\gametypes\_gamelogic::endgame( winners[0], game["end_reason"]["time_limit_reached"] );
else if ( winners[winners.size - 1].gungamegunindex > winners[winners.size - 2].gungamegunindex )
thread maps\mp\gametypes\_gamelogic::endgame( winners[winners.size - 1], game["end_reason"]["time_limit_reached"] );
else
thread maps\mp\gametypes\_gamelogic::endgame( "tie", game["end_reason"]["time_limit_reached"] );
}
gethighestprogressedplayers()
{
var_0 = -1;
var_1 = [];
foreach ( var_3 in level.players )
{
if ( isdefined( var_3.gungamegunindex ) && var_3.gungamegunindex >= var_0 )
{
var_0 = var_3.gungamegunindex;
var_1[var_1.size] = var_3;
}
}
return var_1;
}
refillammo()
{
level endon( "game_ended" );
self endon( "disconnect" );
for (;;)
{
self waittill( "reload" );
self givestartammo( self.primaryweapon );
}
}
refillsinglecountammo()
{
level endon( "game_ended" );
self endon( "disconnect" );
for (;;)
{
if ( maps\mp\_utility::isreallyalive( self ) && self.team != "spectator" && isdefined( self.primaryweapon ) && self getammocount( self.primaryweapon ) == 0 )
{
wait 2;
self notify( "reload" );
wait 1;
continue;
}
wait 0.05;
}
}
setguns()
{
var_0 = getdvarint( "scr_gun_loot_variants", 0 );
level.gun_guns = [];
level.gun_guns[0] = "iw5_asm1";
level.gun_guns[1] = "iw5_asaw";
level.gun_guns[2] = "iw5_himar";
level.gun_guns[3] = "iw5_kf5";
level.gun_guns[4] = "iw5_hbra3";
level.gun_guns[5] = "iw5_mp11";
level.gun_guns[6] = "iw5_ak12";
level.gun_guns[7] = "iw5_sn6";
level.gun_guns[8] = "iw5_arx160";
level.gun_guns[9] = "iw5_hmr9";
level.gun_guns[10] = "iw5_maul";
level.gun_guns[11] = "iw5_dlcgun3";
level.gun_guns[12] = "iw5_em1";
level.gun_guns[13] = "iw5_uts19";
level.gun_guns[14] = "iw5_lsat";
level.gun_guns[15] = "iw5_rhino";
level.gun_guns[16] = "iw5_exoxmg";
level.gun_guns[17] = "iw5_epm3";
level.gun_guns[18] = "iw5_mors";
level.gun_guns[19] = "iw5_rw1";
level.gun_guns[20] = "iw5_vbr";
level.gun_guns[21] = "iw5_pbw";
level.gun_guns[22] = "iw5_thor";
level.gun_guns[23] = "iw5_mahem";
level.gun_guns[24] = "iw5_exocrossbow";
if ( isdefined( var_0 ) && var_0 )
{
for ( var_1 = 0; var_1 < level.gun_guns.size; var_1++ )
{
var_2 = level.gun_guns[var_1];
if ( maps\mp\_utility::getweaponclass( var_2 ) == "weapon_projectile" || maps\mp\_utility::getweaponclass( var_2 ) == "weapon_sec_special" )
var_2 = assign_random_loot_variant( var_2, 4 );
else
var_2 = assign_random_loot_variant( var_2, 10 );
level.gun_guns[var_1] = var_2;
}
}
}
assign_random_loot_variant( var_0, var_1 )
{
var_2 = randomint( var_1 );
switch ( var_2 )
{
case 0:
var_0 += "loot0";
break;
case 1:
var_0 += "loot1";
break;
case 2:
var_0 += "loot2";
break;
case 3:
var_0 += "loot3";
break;
case 4:
var_0 += "loot4";
break;
case 5:
var_0 += "loot5";
break;
case 6:
var_0 += "loot6";
break;
case 7:
var_0 += "loot7";
break;
case 8:
var_0 += "loot8";
break;
case 9:
var_0 += "loot9";
break;
default:
break;
}
return var_0;
}
setspecialloadout()
{
level.gun_loadout = maps\mp\gametypes\_class::getemptyloadout();
if ( maps\mp\gametypes\_class::isvalidprimary( level.gun_guns[0] ) )
level.gun_loadout["loadoutPrimary"] = level.gun_guns[0];
else if ( maps\mp\gametypes\_class::isvalidsecondary( level.gun_guns[0], 0 ) )
level.gun_loadout["loadoutSecondary"] = level.gun_guns[0];
}

View File

@ -0,0 +1,19 @@
if (game:issingleplayer()) then
return
end
function GetGameModeName()
return Engine.Localize(Engine.TableLookup(GameTypesTable.File, GameTypesTable.Cols.Ref, GameX.GetGameMode(), GameTypesTable.Cols.Name))
end
-- Allow players to change teams in game.
function CanChangeTeam()
local f9_local0 = GameX.GetGameMode()
local f9_local1
if f9_local0 ~= "aliens" and Engine.TableLookup( GameTypesTable.File, GameTypesTable.Cols.Ref, f9_local0, GameTypesTable.Cols.TeamChoice ) == "1" then
f9_local1 = not MLG.IsMLGSpectator()
else
f9_local1 = false
end
return f9_local1
end

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit 517ed29228d18cf2c5004d10826090108e06f049
Subproject commit cbf5e664fc55cbcc33b10f3058ede325cd133a01

2
deps/asmjit vendored

@ -1 +1 @@
Subproject commit 0c03ed2f7497441ac0de232bda2e6b8cc041b2dc
Subproject commit e13642567393a4bfc8c5607b33337b2ed3b0d01e

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit 3474ca37124c6fe78f5461876542e226a25b5f1f
Subproject commit 29986d04f2dca985ee64fbca1c7431ea3e3422f4

2
deps/lua vendored

@ -1 +1 @@
Subproject commit be908a7d4d8130264ad67c5789169769f824c5d1
Subproject commit d69789da1ccfa4db7c241de6b471d6b729f1561e

2
deps/minhook vendored

@ -1 +1 @@
Subproject commit 426cb6880035ee3cceed05384bb3f2db01a20a15
Subproject commit 49d03ad118cf7f6768c79a8f187e14b8f2a07f94

2
deps/protobuf vendored

@ -1 +1 @@
Subproject commit 57786d126249b5ed4f42b579047941805e742949
Subproject commit 8d5fdedd42ef361dcfc1531fba4f33470273f375

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit 06d58b9e848c650114556a23294d0b6440078c61
Subproject commit 012be8528783cdbf4b7a9e64f78bd8f056b97e24

2
deps/sol2 vendored

@ -1 +1 @@
Subproject commit f81643aa0c0c507c0cd8400b8cfedc74a34a19f6
Subproject commit 19898d8d3e6c3def33625082343428be1bb9387b

2
deps/zlib vendored

@ -1 +1 @@
Subproject commit 04f42ceca40f73e2978b50e93806c2a18c1281fc
Subproject commit 02a6049eb3884c430268bb0fe3296d597a03174c

View File

@ -41,7 +41,7 @@ namespace branding
}
localized_strings::override("LUA_MENU_LEGAL_COPYRIGHT", "S1x: " VERSION);
dvars::override::Dvar_SetString("version", utils::string::va("S1x %s", VERSION));
dvars::override::set_string("version", utils::string::va("S1x %s", VERSION));
ui_get_formatted_build_number_hook.create(
SELECT_VALUE(0x14035B3F0, 0x1404A8950), ui_get_formatted_build_number_stub);

View File

@ -182,13 +182,13 @@ namespace dedicated
sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE, "Don't send heartbeat");
// Disable VirtualLobby
dvars::override::Dvar_RegisterBool("virtualLobbyEnabled", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
dvars::override::register_bool("virtualLobbyEnabled", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
// Disable r_preloadShaders
dvars::override::Dvar_RegisterBool("r_preloadShaders", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ);
// Don't allow sv_hostname to be changed by the game
dvars::disable::Dvar_SetString("sv_hostname");
dvars::disable::set_string("sv_hostname");
// Stop crashing from sys_errors
utils::hook::jump(0x1404D6260, sys_error_stub);

View File

@ -11,7 +11,6 @@ namespace dvars
struct dvar_base
{
unsigned int flags{};
std::string description{};
};
struct dvar_bool : dvar_base
@ -83,22 +82,22 @@ namespace dvars
static std::unordered_set<std::string> set_int_disables;
static std::unordered_set<std::string> set_string_disables;
void Dvar_SetBool(const std::string& name)
void set_bool(const std::string& name)
{
set_bool_disables.emplace(name);
}
void Dvar_SetFloat(const std::string& name)
void set_float(const std::string& name)
{
set_float_disables.emplace(name);
}
void Dvar_SetInt(const std::string& name)
void set_int(const std::string& name)
{
set_int_disables.emplace(name);
}
void Dvar_SetString(const std::string& name)
void set_string(const std::string& name)
{
set_string_disables.emplace(name);
}
@ -118,54 +117,43 @@ namespace dvars
static std::unordered_map<std::string, int> set_int_overrides;
static std::unordered_map<std::string, std::string> set_string_overrides;
void Dvar_RegisterBool(const std::string& name, const bool value, const unsigned int flags,
const std::string& description)
void register_bool(const std::string& name, const bool value, const unsigned int flags)
{
dvar_bool values;
values.value = value;
values.flags = flags;
values.description = description;
register_bool_overrides[name] = std::move(values);
}
void Dvar_RegisterFloat(const std::string& name, const float value, const float min, const float max,
const unsigned int flags,
const std::string& description)
void register_float(const std::string& name, const float value, const float min, const float max, const unsigned int flags)
{
dvar_float values;
values.value = value;
values.min = min;
values.max = max;
values.flags = flags;
values.description = description;
register_float_overrides[name] = std::move(values);
}
void Dvar_RegisterInt(const std::string& name, const int value, const int min, const int max,
const unsigned int flags,
const std::string& description)
void register_int(const std::string& name, const int value, const int min, const int max, const unsigned int flags)
{
dvar_int values;
values.value = value;
values.min = min;
values.max = max;
values.flags = flags;
values.description = description;
register_int_overrides[name] = std::move(values);
}
void Dvar_RegisterString(const std::string& name, const std::string& value, const unsigned int flags,
const std::string& description)
void register_string(const std::string& name, const std::string& value, const unsigned int flags)
{
dvar_string values;
values.value = value;
values.flags = flags;
values.description = description;
register_string_overrides[name] = std::move(values);
}
void Dvar_RegisterVector2(const std::string& name, float x, float y, float min, float max,
const unsigned int flags, const std::string& description)
void register_vector2(const std::string& name, float x, float y, float min, float max, const unsigned int flags)
{
dvar_vector2 values;
values.x = x;
@ -173,12 +161,10 @@ namespace dvars
values.min = min;
values.max = max;
values.flags = flags;
values.description = description;
register_vector2_overrides[name] = std::move(values);
}
void Dvar_RegisterVector3(const std::string& name, float x, float y, float z, float min,
float max, const unsigned int flags, const std::string& description)
void register_vector3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags)
{
dvar_vector3 values;
values.x = x;
@ -187,26 +173,25 @@ namespace dvars
values.min = min;
values.max = max;
values.flags = flags;
values.description = description;
register_vector3_overrides[name] = std::move(values);
}
void Dvar_SetBool(const std::string& name, const bool value)
void set_bool(const std::string& name, const bool value)
{
set_bool_overrides[name] = value;
}
void Dvar_SetFloat(const std::string& name, const float value)
void set_float(const std::string& name, const float value)
{
set_float_overrides[name] = value;
}
void Dvar_SetInt(const std::string& name, const int value)
void set_int(const std::string& name, const int value)
{
set_int_overrides[name] = value;
}
void Dvar_SetString(const std::string& name, const std::string& value)
void set_string(const std::string& name, const std::string& value)
{
set_string_overrides[name] = value;
}
@ -224,20 +209,19 @@ namespace dvars
utils::hook::detour dvar_set_int_hook;
utils::hook::detour dvar_set_string_hook;
game::dvar_t* dvar_register_bool(const char* name, bool value, unsigned int flags, const char* description)
game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags, const char* description)
{
auto* var = find_dvar(override::register_bool_overrides, name);
if (var)
{
value = var->value;
flags = var->flags;
description = var->description.data();
}
return dvar_register_bool_hook.invoke<game::dvar_t*>(name, value, flags, description);
}
game::dvar_t* dvar_register_float(const char* name, float value, float min, float max, unsigned int flags,
game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags,
const char* description)
{
auto* var = find_dvar(override::register_float_overrides, name);
@ -247,13 +231,12 @@ namespace dvars
min = var->min;
max = var->max;
flags = var->flags;
description = var->description.data();
}
return dvar_register_float_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
}
game::dvar_t* dvar_register_int(const char* name, int value, int min, int max, unsigned int flags,
game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags,
const char* description)
{
auto* var = find_dvar(override::register_int_overrides, name);
@ -263,26 +246,24 @@ namespace dvars
min = var->min;
max = var->max;
flags = var->flags;
description = var->description.data();
}
return dvar_register_int_hook.invoke<game::dvar_t*>(name, value, min, max, flags, description);
}
game::dvar_t* dvar_register_string(const char* name, const char* value, unsigned int flags, const char* description)
game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags, const char* description)
{
auto* var = find_dvar(override::register_string_overrides, name);
if (var)
{
value = var->value.data();
flags = var->flags;
description = var->description.data();
}
return dvar_register_string_hook.invoke<game::dvar_t*>(name, value, flags, description);
}
game::dvar_t* dvar_register_vector2(const char* name, float x, float y, float min, float max,
game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max,
unsigned int flags, const char* description)
{
auto* var = find_dvar(override::register_vector2_overrides, name);
@ -293,13 +274,12 @@ namespace dvars
min = var->min;
max = var->max;
flags = var->flags;
description = var->description.data();
}
return dvar_register_vector2_hook.invoke<game::dvar_t*>(name, x, y, min, max, flags, description);
}
game::dvar_t* dvar_register_vector3(const char* name, float x, float y, float z, float min,
game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min,
float max, unsigned int flags, const char* description)
{
auto* var = find_dvar(override::register_vector3_overrides, name);
@ -311,13 +291,12 @@ namespace dvars
min = var->min;
max = var->max;
flags = var->flags;
description = var->description.data();
}
return dvar_register_vector3_hook.invoke<game::dvar_t*>(name, x, y, z, min, max, flags, description);
}
void dvar_set_bool(game::dvar_t* dvar, bool boolean)
void dvar_set_bool_stub(game::dvar_t* dvar, bool boolean)
{
const auto disabled = find_dvar(disable::set_bool_disables, dvar->name);
if (disabled)
@ -334,7 +313,7 @@ namespace dvars
return dvar_set_bool_hook.invoke<void>(dvar, boolean);
}
void dvar_set_float(game::dvar_t* dvar, float fl)
void dvar_set_float_stub(game::dvar_t* dvar, float fl)
{
const auto disabled = find_dvar(disable::set_float_disables, dvar->name);
if (disabled)
@ -351,7 +330,7 @@ namespace dvars
return dvar_set_float_hook.invoke<void>(dvar, fl);
}
void dvar_set_int(game::dvar_t* dvar, int integer)
void dvar_set_int_stub(game::dvar_t* dvar, int integer)
{
const auto disabled = find_dvar(disable::set_int_disables, dvar->name);
if (disabled)
@ -368,7 +347,7 @@ namespace dvars
return dvar_set_int_hook.invoke<void>(dvar, integer);
}
void dvar_set_string(game::dvar_t* dvar, const char* string)
void dvar_set_string_stub(game::dvar_t* dvar, const char* string)
{
const auto disabled = find_dvar(disable::set_string_disables, dvar->name);
if (disabled)
@ -390,17 +369,17 @@ namespace dvars
public:
void post_unpack() override
{
dvar_register_bool_hook.create(SELECT_VALUE(0x140371850, 0x1404C0BE0), &dvar_register_bool);
dvar_register_float_hook.create(SELECT_VALUE(0x140371C20, 0x1404C0FB0), &dvar_register_float);
dvar_register_int_hook.create(SELECT_VALUE(0x140371CF0, 0x1404C1080), &dvar_register_int);
dvar_register_string_hook.create(SELECT_VALUE(0x140372050, 0x1404C1450), &dvar_register_string);
dvar_register_vector2_hook.create(SELECT_VALUE(0x140372120, 0x1404C1520), &dvar_register_vector2);
dvar_register_vector3_hook.create(SELECT_VALUE(0x140372230, 0x1404C1600), &dvar_register_vector3);
dvar_register_bool_hook.create(SELECT_VALUE(0x140371850, 0x1404C0BE0), &dvar_register_bool_stub);
dvar_register_float_hook.create(SELECT_VALUE(0x140371C20, 0x1404C0FB0), &dvar_register_float_stub);
dvar_register_int_hook.create(SELECT_VALUE(0x140371CF0, 0x1404C1080), &dvar_register_int_stub);
dvar_register_string_hook.create(SELECT_VALUE(0x140372050, 0x1404C1450), &dvar_register_string_stub);
dvar_register_vector2_hook.create(SELECT_VALUE(0x140372120, 0x1404C1520), &dvar_register_vector2_stub);
dvar_register_vector3_hook.create(SELECT_VALUE(0x140372230, 0x1404C1600), &dvar_register_vector3_stub);
dvar_set_bool_hook.create(SELECT_VALUE(0x140372B70, 0x1404C1F30), &dvar_set_bool);
dvar_set_float_hook.create(SELECT_VALUE(0x140373420, 0x1404C2A10), &dvar_set_float);
dvar_set_int_hook.create(SELECT_VALUE(0x1403738D0, 0x1404C2F40), &dvar_set_int);
dvar_set_string_hook.create(SELECT_VALUE(0x140373DE0, 0x1404C3610), &dvar_set_string);
dvar_set_bool_hook.create(SELECT_VALUE(0x140372B70, 0x1404C1F30), &dvar_set_bool_stub);
dvar_set_float_hook.create(SELECT_VALUE(0x140373420, 0x1404C2A10), &dvar_set_float_stub);
dvar_set_int_hook.create(SELECT_VALUE(0x1403738D0, 0x1404C2F40), &dvar_set_int_stub);
dvar_set_string_hook.create(SELECT_VALUE(0x140373DE0, 0x1404C3610), &dvar_set_string_stub);
}
};
}

View File

@ -4,24 +4,24 @@ namespace dvars
{
namespace disable
{
void Dvar_SetBool(const std::string& name);
void Dvar_SetFloat(const std::string& name);
void Dvar_SetInt(const std::string& name);
void Dvar_SetString(const std::string& name);
void set_bool(const std::string& name);
void set_float(const std::string& name);
void set_int(const std::string& name);
void set_string(const std::string& name);
}
namespace override
{
void Dvar_RegisterBool(const std::string& name, bool value, const unsigned int flags, const std::string& description = "");
void Dvar_RegisterFloat(const std::string& name, float value, float min, float max, const unsigned int flags, const std::string& description = "");
void Dvar_RegisterInt(const std::string& name, int value, int min, int max, const unsigned int flags, const std::string& description = "");
void Dvar_RegisterString(const std::string& name, const std::string& value, const unsigned int flags, const std::string& description = "");
void Dvar_RegisterVector2(const std::string& name, float x, float y, float min, float max, const unsigned int flags, const std::string& description = "");
void Dvar_RegisterVector3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags, const std::string& description = "");
void register_bool(const std::string& name, bool value, unsigned int flags);
void register_float(const std::string& name, float value, float min, float max, unsigned int flags);
void register_int(const std::string& name, int value, int min, int max, unsigned int flags);
void register_string(const std::string& name, const std::string& value, unsigned int flags);
void register_vector2(const std::string& name, float x, float y, float min, float max, unsigned int flags);
void register_vector3(const std::string& name, float x, float y, float z, float min, float max, unsigned int flags);
void Dvar_SetBool(const std::string& name, bool boolean);
void Dvar_SetFloat(const std::string& name, float fl);
void Dvar_SetInt(const std::string& name, int integer);
void Dvar_SetString(const std::string& name, const std::string& string);
void set_bool(const std::string& name, bool boolean);
void set_float(const std::string& name, float fl);
void set_int(const std::string& name, int integer);
void set_string(const std::string& name, const std::string& string);
}
}

View File

@ -233,7 +233,7 @@ namespace filesystem
void post_unpack() override
{
// Set fs_basegame
dvars::override::Dvar_RegisterString("fs_basegame", "s1x", game::DVAR_FLAG_WRITE);
dvars::override::register_string("fs_basegame", "s1x", game::DVAR_FLAG_WRITE);
if (game::environment::is_sp())
{

View File

@ -173,7 +173,7 @@ namespace fps
scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer);
if (game::environment::is_mp())
{
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping");
cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping");
scheduler::loop(cg_draw_ping, scheduler::pipeline::renderer);
}

View File

@ -121,7 +121,7 @@ namespace gsc
return nullptr;
}
const auto script_file_ptr = static_cast<game::ScriptFile*>(game::PMem_AllocFromSource_NoDebug(sizeof(game::ScriptFile), 4, 1, 5));
const auto script_file_ptr = static_cast<game::ScriptFile*>(game::Hunk_AllocateTempMemoryHighInternal(sizeof(game::ScriptFile)));
script_file_ptr->name = file_name;
const auto stack = assembler->output_stack();
@ -130,16 +130,15 @@ namespace gsc
const auto script = assembler->output_script();
script_file_ptr->bytecodeLen = static_cast<int>(script.size());
const auto script_size = script.size();
// Use PMem for both stack and byte code
const auto buffer_size = script_size + stack.size() + 2;
const auto stack_size = static_cast<std::uint32_t>(stack.size() + 1);
const auto byte_code_size = static_cast<std::uint32_t>(script.size() + 1);
const auto buffer = static_cast<std::uint8_t*>(game::PMem_AllocFromSource_NoDebug(static_cast<std::uint32_t>(buffer_size), 4, 1, 5));
std::memcpy(buffer, script.data(), script_size);
std::memcpy(&buffer[script_size], stack.data(), stack.size());
script_file_ptr->buffer = static_cast<char*>(game::Hunk_AllocateTempMemoryHighInternal(stack_size));
std::memcpy(const_cast<char*>(script_file_ptr->buffer), stack.data(), stack.size());
script_file_ptr->bytecode = static_cast<std::uint8_t*>(game::PMem_AllocFromSource_NoDebug(byte_code_size, 4, 1, 5));
std::memcpy(script_file_ptr->bytecode, script.data(), script.size());
script_file_ptr->bytecode = &buffer[0];
script_file_ptr->buffer = reinterpret_cast<char*>(&buffer[script.size()]);
script_file_ptr->compressedLen = 0;
loaded_scripts[real_name] = script_file_ptr;

View File

@ -5,35 +5,40 @@
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "map_rotation.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace map_rotation
{
DWORD previousPriority;
namespace
{
void set_dvar(const std::string& dvar, const std::string& value)
{
command::execute(utils::string::va("%s \"%s\"", dvar.data(), value.data()), true);
}
rotation_data dedicated_rotation;
const game::dvar_t* sv_map_rotation;
const game::dvar_t* sv_map_rotation_current;
const game::dvar_t* sv_random_map_rotation;
void set_gametype(const std::string& gametype)
{
set_dvar("g_gametype", gametype);
assert(!gametype.empty());
auto* g_gametype = game::Dvar_FindVar("g_gametype");
game::Dvar_SetString(g_gametype, gametype.data());
}
void launch_map(const std::string& mapname)
{
assert(!mapname.empty());
command::execute(utils::string::va("map %s", mapname.data()), false);
}
void launch_default_map()
{
auto* mapname = game::Dvar_FindVar("mapname");
if (mapname && mapname->current.string && strlen(mapname->current.string) && mapname->current.string !=
"mp_vlobby_room"s)
if (mapname && std::strcmp(mapname->current.string, "mp_vlobby_room") != 0)
{
launch_map(mapname->current.string);
}
@ -50,55 +55,113 @@ namespace map_rotation
}
}
std::string load_current_map_rotation()
void apply_rotation(rotation_data& rotation)
{
auto* rotation = game::Dvar_FindVar("sv_mapRotationCurrent");
if (!strlen(rotation->current.string))
assert(!rotation.empty());
std::size_t i = 0;
while (i < rotation.get_entries_size())
{
rotation = game::Dvar_FindVar("sv_mapRotation");
set_dvar("sv_mapRotationCurrent", rotation->current.string);
}
return rotation->current.string;
}
std::vector<std::string> parse_current_map_rotation()
{
const auto rotation = load_current_map_rotation();
return utils::string::split(rotation, ' ');
}
void store_new_rotation(const std::vector<std::string>& elements, const size_t index)
{
std::string value{};
for (auto i = index; i < elements.size(); ++i)
{
if (i != index)
const auto& entry = rotation.get_next_entry();
if (entry.first == "map"s)
{
value.push_back(' ');
console::info("Loading new map: '%s'\n", entry.second.data());
if (!game::SV_MapExists(entry.second.data()))
{
console::info("map_rotation: '%s' map doesn't exist!\n", entry.second.data());
launch_default_map();
return;
}
launch_map(entry.second);
// Map was found so we exit the loop
break;
}
value.append(elements[i]);
if (entry.first == "gametype"s)
{
console::info("Applying new gametype: '%s'\n", entry.second.data());
set_gametype(entry.second);
}
++i;
}
set_dvar("sv_mapRotationCurrent", value);
if (i == rotation.get_entries_size())
{
console::error("Map rotation does not contain any map. Restarting\n");
launch_default_map();
}
}
void change_process_priority()
void load_rotation(const std::string& data)
{
auto* const dvar = game::Dvar_FindVar("sv_autoPriority");
if (dvar && dvar->current.enabled)
static auto loaded = false;
if (loaded)
{
scheduler::on_game_initialized([]()
{
//printf("=======================setting OLD priority=======================\n");
SetPriorityClass(GetCurrentProcess(), previousPriority);
}, scheduler::pipeline::main, 1s);
return;
}
previousPriority = GetPriorityClass(GetCurrentProcess());
//printf("=======================setting NEW priority=======================\n");
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
loaded = true;
try
{
dedicated_rotation.parse(data);
}
catch (const std::exception& ex)
{
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name);
}
#ifdef _DEBUG
console::info("dedicated_rotation size after parsing is '%llu'", dedicated_rotation.get_entries_size());
#endif
}
void load_map_rotation()
{
const std::string map_rotation = sv_map_rotation->current.string;
if (!map_rotation.empty())
{
#ifdef _DEBUG
console::info("%s is not empty. Parsing...\n", sv_map_rotation->name);
#endif
load_rotation(map_rotation);
}
}
void apply_map_rotation_current(const std::string& data)
{
assert(!data.empty());
rotation_data rotation_current;
try
{
rotation_current.parse(data);
}
catch (const std::exception& ex)
{
console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation_current->name);
}
game::Dvar_SetString(sv_map_rotation_current, "");
if (rotation_current.empty())
{
console::warn("%s is empty or contains invalid data\n", sv_map_rotation_current->name);
launch_default_map();
return;
}
apply_rotation(rotation_current);
}
void randomize_map_rotation()
{
if (sv_random_map_rotation->current.enabled)
{
console::info("Randomizing the map rotation\n");
dedicated_rotation.randomize();
}
}
@ -110,42 +173,35 @@ namespace map_rotation
return;
}
const auto rotation = parse_current_map_rotation();
console::info("Rotating map...\n");
for (size_t i = 0; !rotation.empty() && i < (rotation.size() - 1); i += 2)
// This takes priority because of backwards compatibility
const std::string map_rotation_current = sv_map_rotation_current->current.string;
if (!map_rotation_current.empty())
{
const auto& key = rotation[i];
const auto& value = rotation[i + 1];
if (key == "gametype")
{
set_gametype(value);
}
else if (key == "map")
{
store_new_rotation(rotation, i + 2);
change_process_priority();
if (!game::SV_MapExists(value.data()))
{
console::info("map_rotation: '%s' map doesn't exist!\n", value.data());
launch_default_map();
return;
}
launch_map(value);
return;
}
else
{
console::info("Invalid map rotation key: %s\n", key.data());
}
#ifdef _DEBUG
console::info("Applying %s\n", sv_map_rotation_current->name);
#endif
apply_map_rotation_current(map_rotation_current);
return;
}
launch_default_map();
load_map_rotation();
if (dedicated_rotation.empty())
{
console::warn("%s is empty or contains invalid data. Restarting map\n", sv_map_rotation->name);
launch_default_map();
return;
}
randomize_map_rotation();
apply_rotation(dedicated_rotation);
}
void trigger_map_rotation()
{
scheduler::schedule([]()
scheduler::schedule([]
{
if (game::CL_IsCgameInitialized())
{
@ -159,6 +215,68 @@ namespace map_rotation
}
rotation_data::rotation_data()
: index_(0)
{
}
void rotation_data::randomize()
{
std::random_device rd;
std::mt19937 gen(rd());
std::ranges::shuffle(this->rotation_entries_, gen);
}
void rotation_data::add_entry(const std::string& key, const std::string& value)
{
this->rotation_entries_.emplace_back(std::make_pair(key, value));
}
bool rotation_data::contains(const std::string& key, const std::string& value) const
{
return std::ranges::any_of(this->rotation_entries_, [&](const auto& entry)
{
return entry.first == key && entry.second == value;
});
}
bool rotation_data::empty() const noexcept
{
return this->rotation_entries_.empty();
}
std::size_t rotation_data::get_entries_size() const noexcept
{
return this->rotation_entries_.size();
}
rotation_data::rotation_entry& rotation_data::get_next_entry()
{
const auto index = this->index_;
++this->index_ %= this->rotation_entries_.size();
return this->rotation_entries_.at(index);
}
void rotation_data::parse(const std::string& data)
{
const auto tokens = utils::string::split(data, ' ');
for (std::size_t i = 0; !tokens.empty() && i < (tokens.size() - 1); i += 2)
{
const auto& key = tokens[i];
const auto& value = tokens[i + 1];
if (key == "map"s || key == "gametype"s)
{
this->add_entry(key, value);
}
else
{
throw map_rotation_parse_error();
}
}
}
class component final : public component_interface
{
public:
@ -169,19 +287,18 @@ namespace map_rotation
return;
}
scheduler::once([]()
scheduler::once([]
{
game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE, "");
game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE, "");
game::Dvar_RegisterBool("sv_autoPriority", true, game::DVAR_FLAG_NONE, "Lowers the process priority during map changes to not cause lags on other servers.");
sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE, "");
sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE, "");
}, scheduler::pipeline::main);
sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE, "Randomize map rotation");
command::add("map_rotate", &perform_map_rotation);
// Hook GScr_ExitLevel
utils::hook::jump(0x14032E490, &trigger_map_rotation);
previousPriority = GetPriorityClass(GetCurrentProcess());
}
};
}

View File

@ -0,0 +1,34 @@
#pragma once
namespace map_rotation
{
struct map_rotation_parse_error : public std::exception
{
[[nodiscard]] const char* what() const noexcept override { return "Map Rotation Parse Erro"; }
};
class rotation_data
{
public:
using rotation_entry = std::pair<std::string, std::string>;
rotation_data();
void randomize();
// In case a new way to enrich the map rotation is added (other than sv_mapRotation)
// this method should be called to add a new entry (gamemode/map & value)
void add_entry(const std::string& key, const std::string& value);
[[nodiscard]] bool contains(const std::string& key, const std::string& value) const;
[[nodiscard]] bool empty() const noexcept;
[[nodiscard]] std::size_t get_entries_size() const noexcept;
[[nodiscard]] rotation_entry& get_next_entry();
void parse(const std::string& data);
private:
std::vector<rotation_entry> rotation_entries_;
std::size_t index_;
};
}

View File

@ -243,10 +243,10 @@ namespace network
utils::hook::call(0x140439D98, &net_compare_address);
// increase cl_maxpackets
dvars::override::Dvar_RegisterInt("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED);
dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED);
// increase snaps
dvars::override::Dvar_RegisterInt("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE);
dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE);
// ignore impure client
utils::hook::jump(0x14043AC0D, reinterpret_cast<void*>(0x14043ACA3));

View File

@ -187,13 +187,15 @@ namespace notifies
message.erase(message.begin());
}
scheduler::once([params, message, client_num]
scheduler::once([params, message, msg_index, client_num]
{
const scripting::entity level{*game::levelEntityId};
const auto player = scripting::call("getEntByNum", {client_num}).as<scripting::entity>();
// Remove \x1F before sending the notify only if present
const auto notify_msg = msg_index ? message.substr(1) : message;
notify(level, params[0], {player, message});
notify(player, params[0], {message});
notify(level, params[0], {player, notify_msg});
notify(player, params[0], {notify_msg});
game_log::g_log_printf("%s;%s;%i;%s;%s\n",
params[0],

View File

@ -286,15 +286,15 @@ namespace patches
utils::hook::set(0x14023BDC0, 0xC3C033);
// disable emblems
dvars::override::Dvar_RegisterInt("emblems_active", 0, 0, 0, game::DVAR_FLAG_NONE);
dvars::override::register_int("emblems_active", 0, 0, 0, game::DVAR_FLAG_NONE);
utils::hook::set<uint8_t>(0x140479590, 0xC3); // don't register commands
// disable elite_clan
dvars::override::Dvar_RegisterInt("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE);
dvars::override::register_int("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE);
utils::hook::set<uint8_t>(0x14054AB20, 0xC3); // don't register commands
// disable codPointStore
dvars::override::Dvar_RegisterInt("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
dvars::override::register_int("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
// don't register every replicated dvar as a network dvar
utils::hook::nop(0x1403534BE, 5); // dvar_foreach
@ -309,27 +309,27 @@ namespace patches
utils::hook::set<uint8_t>(0x14019B9B9, 0xEB);
// some anti tamper thing that kills performance
dvars::override::Dvar_RegisterInt("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
// unlock safeArea_*
utils::hook::jump(0x140219F5E, 0x140219F67);
utils::hook::jump(0x140219F80, 0x140219F8E);
dvars::override::Dvar_RegisterFloat("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::Dvar_RegisterFloat("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::Dvar_RegisterFloat("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::Dvar_RegisterFloat("safeArea_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
dvars::override::register_float("safeArea_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
// move chat position on the screen above menu splashes
dvars::override::Dvar_RegisterVector2("cg_hudChatPosition", 5, 170, 0, 640, game::DVAR_FLAG_SAVED);
dvars::override::register_vector2("cg_hudChatPosition", 5, 170, 0, 640, game::DVAR_FLAG_SAVED);
// allow servers to check for new packages more often
dvars::override::Dvar_RegisterInt("sv_network_fps", 1000, 20, 1000, game::DVAR_FLAG_SAVED);
dvars::override::register_int("sv_network_fps", 1000, 20, 1000, game::DVAR_FLAG_SAVED);
// Massively increate timeouts
dvars::override::Dvar_RegisterInt("cl_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // Seems unused
dvars::override::Dvar_RegisterInt("sv_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // 30 - 0 - 1800
dvars::override::Dvar_RegisterInt("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
dvars::override::Dvar_RegisterInt("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
dvars::override::register_int("cl_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // Seems unused
dvars::override::register_int("sv_timeout", 90, 90, 1800, game::DVAR_FLAG_NONE); // 30 - 0 - 1800
dvars::override::register_int("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused
dvars::override::register_int("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800
game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, "");

View File

@ -23,12 +23,12 @@ namespace ranked
if (game::environment::is_mp())
{
dvars::override::Dvar_RegisterBool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED);
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED);
}
if (game::environment::is_dedi() && !utils::flags::has_flag("unranked"))
{
dvars::override::Dvar_RegisterBool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
// Some dvar used in gsc
game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE, "Force ranking");

View File

@ -91,6 +91,7 @@ namespace scripting
{
script_function_table_sort.clear();
script_function_table.clear();
script_function_table_rev.clear();
canonical_string_table.clear();
}

View File

@ -65,7 +65,8 @@ namespace game
WEAK symbol<bool(const char* name)> Dvar_IsValidName{0x140370CB0, 0x1404BFF70};
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x140372950, 0x1404C1DB0};
WEAK symbol<void(const char* dvar, const char* buffer)> Dvar_SetCommand{0x1403730D0, 0x1404C2520};
WEAK symbol<void(dvar_t* dvar, const char* string)> Dvar_SetString{0x140373DE0, 0x1404C3610};
WEAK symbol<void(const dvar_t* dvar, const char* string)> Dvar_SetString{0x140373DE0, 0x1404C3610};
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x0, 0x1404C1F30};
WEAK symbol<void(const char*, const char*, DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40};
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x140374E10, 0x1404C47B0};
@ -217,8 +218,8 @@ namespace game
WEAK symbol<const char*()> SEH_GetCurrentLanguageName{0x140339300, 0x1404745C0};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x0, 0x1404C7BA0};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x0, 0x1404B68B0};
WEAK symbol<void*(unsigned int size, unsigned int alignment, unsigned int type, int source)> PMem_AllocFromSource_NoDebug{0x1403775F0, 0x1404C7BA0};
WEAK symbol<void*(unsigned int size)> Hunk_AllocateTempMemoryHighInternal{0x140369D60, 0x1404B68B0};
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x14059C5C0, 0x1406FD930};
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x14059CD00, 0x1406FE070};

View File

@ -2,41 +2,65 @@
#include "html_frame.hpp"
#include <utils/nt.hpp>
#include <utils/hook.hpp>
std::atomic<int> html_frame::frame_count_ = 0;
namespace
{
void* original_func{};
GUID browser_emulation_guid{ 0xac969931, 0x3566, 0x4b50, {0xae, 0x48, 0x71, 0xb9, 0x6a, 0x75, 0xc8, 0x79} };
int WINAPI co_internet_feature_value_internal_stub(const GUID* guid, uint32_t* result)
{
const auto res = static_cast<decltype(co_internet_feature_value_internal_stub)*>(original_func)(guid, result);
if (IsEqualGUID(*guid, browser_emulation_guid))
{
*result = 11000;
return 0;
}
return res;
}
void setup_ie_hook()
{
static const auto _ = []
{
const auto urlmon = utils::nt::library::load("urlmon.dll"s);
const auto target = urlmon.get_iat_entry("iertutil.dll", MAKEINTRESOURCEA(700));
original_func = *target;
utils::hook::set(target, co_internet_feature_value_internal_stub);
return 0;
}();
(void)_;
}
}
html_frame::callback_params::callback_params(DISPPARAMS* params, VARIANT* res) : result(res)
{
for (auto i = params->cArgs; i > 0; --i)
{
auto* param = &params->rgvarg[i - 1];
auto param = &params->rgvarg[i - 1];
this->arguments.emplace_back(param);
}
}
html_frame::html_frame()
: in_place_frame_(this)
, in_place_site_(this)
, ui_handler_(this)
, client_site_(this)
, html_dispatch_(this)
html_frame::html_frame() : in_place_frame_(this), in_place_site_(this), ui_handler_(this), client_site_(this),
html_dispatch_(this)
{
setup_ie_hook();
if (frame_count_++ == 0 && OleInitialize(nullptr) != S_OK)
{
throw std::runtime_error("Unable to initialize the OLE library");
}
auto needs_restart = false;
needs_restart |= set_browser_feature("FEATURE_BROWSER_EMULATION", 11000);
needs_restart |= set_browser_feature("FEATURE_GPU_RENDERING", 1);
if (needs_restart)
{
utils::nt::relaunch_self();
utils::nt::terminate(0);
}
set_browser_feature("FEATURE_BROWSER_EMULATION", 11000);
set_browser_feature("FEATURE_GPU_RENDERING", 1);
}
html_frame::~html_frame()
{
if (--frame_count_ <= 0)

View File

@ -55,25 +55,27 @@
#undef min
#endif
#include <ranges>
#include <map>
#include <atomic>
#include <vector>
#include <mutex>
#include <queue>
#include <regex>
#include <cassert>
#include <chrono>
#include <thread>
#include <fstream>
#include <iostream>
#include <utility>
#include <filesystem>
#include <functional>
#include <sstream>
#include <optional>
#include <unordered_set>
#include <variant>
#include <format>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <mutex>
#include <optional>
#include <queue>
#include <random>
#include <ranges>
#include <regex>
#include <sstream>
#include <thread>
#include <unordered_set>
#include <utility>
#include <variant>
#include <vector>
#include <gsl/gsl>
#include <udis86.h>

View File

@ -76,10 +76,7 @@ namespace utils
void memory::free(void* data)
{
if (data)
{
std::free(data);
}
std::free(data);
}
void memory::free(const void* data)

View File

@ -15,7 +15,8 @@ namespace utils::nt
library library::get_by_address(void* address)
{
HMODULE handle = nullptr;
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, static_cast<LPCSTR>(address), &handle);
GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
static_cast<LPCSTR>(address), &handle);
return library(handle);
}
@ -157,6 +158,11 @@ namespace utils::nt
}
void** library::get_iat_entry(const std::string& module_name, const std::string& proc_name) const
{
return this->get_iat_entry(module_name, proc_name.data());
}
void** library::get_iat_entry(const std::string& module_name, const char* proc_name) const
{
if (!this->is_valid()) return nullptr;
@ -166,7 +172,7 @@ namespace utils::nt
auto* const target_function = other_module.get_proc<void*>(proc_name);
if (!target_function) return nullptr;
auto* header = this->get_optional_header();
const auto* header = this->get_optional_header();
if (!header) return nullptr;
auto* import_descriptor = reinterpret_cast<PIMAGE_IMPORT_DESCRIPTOR>(this->get_ptr() + header->DataDirectory
@ -183,16 +189,22 @@ namespace utils::nt
while (original_thunk_data->u1.AddressOfData)
{
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
if (ordinal_number > 0xFFFF) continue;
if (GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number)) ==
target_function)
if (thunk_data->u1.Function == reinterpret_cast<uint64_t>(target_function))
{
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
const size_t ordinal_number = original_thunk_data->u1.AddressOfData & 0xFFFFFFF;
if (ordinal_number <= 0xFFFF)
{
auto* proc = GetProcAddress(other_module.module_, reinterpret_cast<char*>(ordinal_number));
if (reinterpret_cast<void*>(proc) == target_function)
{
return reinterpret_cast<void**>(&thunk_data->u1.Function);
}
}
++original_thunk_data;
++thunk_data;
}

View File

@ -59,6 +59,13 @@ namespace utils::nt
return reinterpret_cast<T>(GetProcAddress(this->module_, process.data()));
}
template <typename T>
T get_proc(const char* name) const
{
if (!this->is_valid()) T{};
return reinterpret_cast<T>(GetProcAddress(this->module_, name));
}
template <typename T>
std::function<T> get(const std::string& process) const
{
@ -97,6 +104,7 @@ namespace utils::nt
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
void** get_iat_entry(const std::string& module_name, const std::string& proc_name) const;
void** get_iat_entry(const std::string& module_name, const char* name) const;
private:
HMODULE module_;

View File

@ -34,24 +34,26 @@ namespace utils::string
return elems;
}
std::string to_lower(std::string text)
std::string to_lower(const std::string& text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
std::string result;
std::ranges::transform(text, std::back_inserter(result), [](const unsigned char input)
{
return static_cast<char>(std::tolower(input));
});
return text;
return result;
}
std::string to_upper(std::string text)
std::string to_upper(const std::string& text)
{
std::transform(text.begin(), text.end(), text.begin(), [](const unsigned char input)
std::string result;
std::ranges::transform(text, std::back_inserter(result), [](const unsigned char input)
{
return static_cast<char>(std::toupper(input));
});
return text;
return result;
}
bool starts_with(const std::string& text, const std::string& substring)

View File

@ -2,10 +2,8 @@
#include "memory.hpp"
#include <cstdint>
#ifndef ARRAYSIZE
template <class Type, size_t n>
size_t ARRAYSIZE(Type (&)[n]) { return n; }
#endif
constexpr auto ARRAY_COUNT(Type(&)[n]) { return n; }
namespace utils::string
{
@ -21,7 +19,7 @@ namespace utils::string
char* get(const char* format, const va_list ap)
{
++this->current_buffer_ %= ARRAYSIZE(this->string_pool_);
++this->current_buffer_ %= ARRAY_COUNT(this->string_pool_);
auto entry = &this->string_pool_[this->current_buffer_];
if (!entry->size || !entry->buffer)
@ -82,8 +80,8 @@ namespace utils::string
std::vector<std::string> split(const std::string& s, char delim);
std::string to_lower(std::string text);
std::string to_upper(std::string text);
std::string to_lower(const std::string& text);
std::string to_upper(const std::string& text);
bool starts_with(const std::string& text, const std::string& substring);
bool ends_with(const std::string& text, const std::string& substring);