Merge branch 'main' into create-a-class
This commit is contained in:
commit
ca1ce5aba0
@ -7,11 +7,11 @@
|
||||
|
||||
# BOIII ☄️
|
||||
|
||||
Reverse engineering and analysis of Call of Duty: Black Ops 3.
|
||||
An attempt at reverse engineering and analyzing Call of Duty: Black Ops 3.
|
||||
|
||||
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQeSXYzQITJrcjiifN1nqX1fsVE7VwLZ3vl2g&usqp=CAU">
|
||||
|
||||
## Roadmap
|
||||
## Technical Features
|
||||
|
||||
- [x] Steam API Emulation
|
||||
- [x] Steam Integrity Bypass
|
||||
@ -24,6 +24,8 @@ Reverse engineering and analysis of Call of Duty: Black Ops 3.
|
||||
- [x] P2P multiplayer
|
||||
- [x] Dedicated Servers
|
||||
|
||||
Check out the <a href="https://github.com/momo5502/boiii/issues?q=is%3Aissue+is%3Aclosed+reason%3Acompleted">closed issues</a> for more gameplay related features and fixes that have been added!
|
||||
|
||||
## Writeups & Articles
|
||||
|
||||
- <a href="https://momo5502.com/posts/2022-11-17-reverse-engineering-integrity-checks-in-black-ops-3/">Reverse engineering integrity checks in Black Ops 3</a>
|
||||
|
26
data/gamesettings/mp/gamesettings_escort.cfg
Normal file
26
data/gamesettings/mp/gamesettings_escort.cfg
Normal file
@ -0,0 +1,26 @@
|
||||
gametype_setting timelimit 5
|
||||
gametype_setting scorelimit 0
|
||||
gametype_setting roundscorelimit 1
|
||||
gametype_setting roundwinlimit 2
|
||||
gametype_setting roundlimit 2
|
||||
gametype_setting preroundperiod 10
|
||||
gametype_setting teamCount 2
|
||||
|
||||
gametype_setting shutdownDamage 3
|
||||
gametype_setting bootTime 5
|
||||
gametype_setting rebootTime 15
|
||||
gametype_setting rebootPlayers 0
|
||||
gametype_setting movePlayers 1
|
||||
gametype_setting robotSpeed 1
|
||||
gametype_setting robotShield 0
|
||||
|
||||
|
||||
gametype_setting scoreHeroPowerGainFactor 0.788 //Score earned towards Hero Weapons and Abilities are multiplied by this factor
|
||||
gametype_setting scoreHeroPowerTimeFactor 0.788
|
||||
|
||||
gametype_setting spawntraptriggertime 5
|
||||
|
||||
gametype_setting disableVehicleSpawners 1
|
||||
|
||||
gametype_setting gameAdvertisementRuleTimeLeft 3.5
|
||||
gametype_setting gameAdvertisementRuleRound 3
|
Binary file not shown.
@ -3,11 +3,9 @@
|
||||
#using scripts\shared\callbacks_shared;
|
||||
#using scripts\shared\system_shared;
|
||||
|
||||
#insert scripts\shared\shared.gsh;
|
||||
|
||||
#namespace serversettings;
|
||||
|
||||
REGISTER_SYSTEM( "serversettings", &__init__, undefined )
|
||||
function autoexec __init__sytem__() { system::register("serversettings",&__init__,undefined,undefined); }
|
||||
|
||||
function __init__()
|
||||
{
|
||||
@ -21,26 +19,29 @@ function init()
|
||||
level.hostname = "CoDHost";
|
||||
SetDvar("sv_hostname", level.hostname);
|
||||
SetDvar("ui_hostname", level.hostname);
|
||||
//makeDvarServerInfo("ui_hostname", "CoDHost");
|
||||
|
||||
level.motd = GetDvarString( "scr_motd" );
|
||||
if(level.motd == "")
|
||||
level.motd = "";
|
||||
SetDvar("scr_motd", level.motd);
|
||||
SetDvar("ui_motd", level.motd);
|
||||
//makeDvarServerInfo("ui_motd", "");
|
||||
|
||||
level.allowvote = GetDvarString( "g_allowvote");
|
||||
level.allowvote = GetDvarString( "g_allowvote" );
|
||||
if(level.allowvote == "")
|
||||
level.allowvote = "1";
|
||||
SetDvar("g_allowvote", level.allowvote);
|
||||
SetDvar("ui_allowvote", level.allowvote);
|
||||
//makeDvarServerInfo("ui_allowvote", "1");
|
||||
|
||||
level.allow_teamchange = "1";
|
||||
SetDvar("ui_allow_teamchange", level.allow_teamchange);
|
||||
|
||||
|
||||
level.friendlyfire = GetGametypeSetting( "friendlyfiretype" );
|
||||
|
||||
SetDvar("ui_friendlyfire", level.friendlyfire);
|
||||
//makeDvarServerInfo("ui_friendlyfire", "0");
|
||||
|
||||
if(GetDvarString( "scr_mapsize") == "")
|
||||
SetDvar("scr_mapsize", "64");
|
||||
@ -57,6 +58,8 @@ function init()
|
||||
constrain_gametype(GetDvarString( "g_gametype"));
|
||||
constrain_map_size(level.mapsize);
|
||||
|
||||
thread setup_callbacks();
|
||||
|
||||
for(;;)
|
||||
{
|
||||
update();
|
||||
@ -80,7 +83,7 @@ function update()
|
||||
SetDvar("ui_motd", level.motd);
|
||||
}
|
||||
|
||||
g_allowvote = GetDvarString( "g_allowvote");
|
||||
g_allowvote = GetDvarString( "g_allowvote" );
|
||||
if(level.allowvote != g_allowvote)
|
||||
{
|
||||
level.allowvote = g_allowvote;
|
||||
@ -194,3 +197,13 @@ function constrain_map_size(mapsize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setup_callbacks()
|
||||
{
|
||||
level.onForfeit = &default_onForfeit;
|
||||
}
|
||||
|
||||
function default_onForfeit()
|
||||
{
|
||||
level.gameForfeited = false;
|
||||
}
|
87
data/ui_scripts/frontend_menus/__init__.lua
Normal file
87
data/ui_scripts/frontend_menus/__init__.lua
Normal file
@ -0,0 +1,87 @@
|
||||
if Engine.GetCurrentMap() ~= "core_frontend" then
|
||||
return
|
||||
end
|
||||
|
||||
local utils = require("utils")
|
||||
|
||||
CoD.LobbyButtons.MP_STATS = {
|
||||
stringRef = "STATS",
|
||||
action = function(self, element, controller, param, menu)
|
||||
SetPerControllerTableProperty(controller, "disableGameSettingsOptions", true)
|
||||
OpenPopup(menu, "BoiiiStatsMenu", controller)
|
||||
end,
|
||||
customId = "btnMPStats"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.MP_START_GAME = {
|
||||
stringRef = "MENU_START_GAME_CAPS",
|
||||
action = function(self, element, controller, param, menu)
|
||||
Engine.SetDvar("party_minplayers", 1)
|
||||
Engine.Exec(nil, "launchgame")
|
||||
end,
|
||||
customId = "btnStartGame"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.SETTING_UP_BOTS = {
|
||||
stringRef = "MENU_SETUP_BOTS_CAPS",
|
||||
action = function(self, element, controller, param, menu)
|
||||
SetPerControllerTableProperty(controller, "disableGameSettingsOptions", true)
|
||||
OpenPopup(menu, "GameSettings_Bots", controller)
|
||||
end,
|
||||
customId = "btnSettingUpBots"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.MP_CUSTOM_SETUP_GAME = {
|
||||
stringRef = "MPUI_SETUP_GAME_CAPS",
|
||||
action = OpenSetupGameMP,
|
||||
customId = "btnSetupGame",
|
||||
}
|
||||
|
||||
local shouldShowMapVote = false
|
||||
local addCustomButtons = function(controller, menuId, buttonTable, isLeader)
|
||||
if menuId == LobbyData.UITargets.UI_MPLOBBYONLINE.id or menuId == LobbyData.UITargets.UI_ZMLOBBYONLINE.id then
|
||||
utils.AddSpacer(buttonTable)
|
||||
utils.AddSmallButton(controller, buttonTable, CoD.LobbyButtons.MP_STATS)
|
||||
end
|
||||
|
||||
if menuId == LobbyData.UITargets.UI_MPLOBBYONLINE.id or menuId == LobbyData.UITargets.UI_ZMLOBBYONLINE.id or menuId == LobbyData.UITargets.UI_MPLOBBYMAIN.id or menuId == LobbyData.UITargets.UI_MPLOBBYLANGAME.id then
|
||||
Engine.Mods_Lists_UpdateUsermaps()
|
||||
end
|
||||
|
||||
if menuId == LobbyData.UITargets.UI_MPLOBBYONLINE.id then
|
||||
shouldShowMapVote = true
|
||||
elseif menuId == LobbyData.UITargets.UI_MPLOBBYONLINEPUBLICGAME.id then
|
||||
if shouldShowMapVote == true then
|
||||
shouldShowMapVote = false
|
||||
--Enable map vote at start lobby
|
||||
Engine.Exec(nil, "LobbyStopDemo")
|
||||
end
|
||||
utils.AddLargeButton(controller, buttonTable, CoD.LobbyButtons.MP_START_GAME, 1) --Launch match button
|
||||
utils.AddSpacer(buttonTable, 1)
|
||||
|
||||
utils.AddSpacer(buttonTable)
|
||||
utils.AddSmallButton(controller, buttonTable, CoD.LobbyButtons.MP_CUSTOM_SETUP_GAME) --Setup game in public lobby
|
||||
elseif menuId == LobbyData.UITargets.UI_MPLOBBYONLINEARENAGAME.id then
|
||||
utils.AddSpacer(buttonTable)
|
||||
utils.AddSmallButton(controller, buttonTable, CoD.LobbyButtons.SETTING_UP_BOTS) --Bot setting button in public lobby
|
||||
end
|
||||
end
|
||||
|
||||
local oldAddButtonsForTarget = CoD.LobbyMenus.AddButtonsForTarget
|
||||
CoD.LobbyMenus.AddButtonsForTarget = function(controller, id)
|
||||
local model = nil
|
||||
if Engine.IsLobbyActive(Enum.LobbyType.LOBBY_TYPE_GAME) then
|
||||
model = Engine.GetModel(DataSources.LobbyRoot.getModel(controller), "gameClient.isHost")
|
||||
else
|
||||
model = Engine.GetModel(DataSources.LobbyRoot.getModel(controller), "privateClient.isHost")
|
||||
end
|
||||
local isLeader = nil
|
||||
if model ~= nil then
|
||||
isLeader = Engine.GetModelValue(model)
|
||||
else
|
||||
isLeader = 1
|
||||
end
|
||||
local result = oldAddButtonsForTarget(controller, id)
|
||||
addCustomButtons(controller, id, result, isLeader)
|
||||
return result
|
||||
end
|
129
data/ui_scripts/frontend_menus/utils.lua
Normal file
129
data/ui_scripts/frontend_menus/utils.lua
Normal file
@ -0,0 +1,129 @@
|
||||
local IsGamescomDemo = function()
|
||||
return Dvar.ui_execdemo_gamescom:get()
|
||||
end
|
||||
|
||||
local IsBetaDemo = function()
|
||||
return Dvar.ui_execdemo_beta:get()
|
||||
end
|
||||
|
||||
local SetButtonState = function(button, state)
|
||||
if state == nil then
|
||||
return
|
||||
elseif state == CoD.LobbyButtons.DISABLED then
|
||||
button.disabled = true
|
||||
elseif state == CoD.LobbyButtons.HIDDEN then
|
||||
button.hidden = true
|
||||
end
|
||||
end
|
||||
|
||||
local AddButton = function(controller, options, button, isLargeButton, index)
|
||||
if button == nil then
|
||||
return
|
||||
end
|
||||
|
||||
button.disabled = false
|
||||
button.hidden = false
|
||||
button.selected = false
|
||||
button.warning = false
|
||||
if button.defaultState ~= nil then
|
||||
if button.defaultState == CoD.LobbyButtons.DISABLED then
|
||||
button.disabled = true
|
||||
elseif button.defaultState == CoD.LobbyButtons.HIDDEN then
|
||||
button.hidden = true
|
||||
end
|
||||
end
|
||||
if button.disabledFunc ~= nil then
|
||||
button.disabled = button.disabledFunc(controller)
|
||||
end
|
||||
if button.visibleFunc ~= nil then
|
||||
button.hidden = not button.visibleFunc(controller)
|
||||
end
|
||||
if IsBetaDemo() then
|
||||
SetButtonState(button, button.demo_beta)
|
||||
elseif IsGamescomDemo() then
|
||||
SetButtonState(button, button.demo_gamescom)
|
||||
end
|
||||
if button.hidden then
|
||||
return
|
||||
end
|
||||
local lobbyNav = LobbyData.GetLobbyNav()
|
||||
if button.selectedFunc ~= nil then
|
||||
button.selected = button.selectedFunc(button.selectedParam)
|
||||
elseif CoD.LobbyMenus.History[lobbyNav] ~= nil then
|
||||
button.selected = CoD.LobbyMenus.History[lobbyNav] == button.customId
|
||||
end
|
||||
if button.newBreadcrumbFunc then
|
||||
local f8_local1 = button.newBreadcrumbFunc
|
||||
if type(f8_local1) == "string" then
|
||||
f8_local1 = LUI.getTableFromPath(f8_local1)
|
||||
end
|
||||
if f8_local1 then
|
||||
button.isBreadcrumbNew = f8_local1(controller)
|
||||
end
|
||||
end
|
||||
if button.warningFunc ~= nil then
|
||||
button.warning = button.warningFunc(controller)
|
||||
end
|
||||
if button.starterPack == CoD.LobbyButtons.STARTERPACK_UPGRADE then
|
||||
button.starterPackUpgrade = true
|
||||
if IsStarterPack() then
|
||||
button.disabled = false
|
||||
end
|
||||
end
|
||||
if index ~= nil then
|
||||
table.insert(options, index, {
|
||||
optionDisplay = button.stringRef,
|
||||
action = button.action,
|
||||
param = button.param,
|
||||
customId = button.customId,
|
||||
isLargeButton = isLargeButton,
|
||||
isLastButtonInGroup = false,
|
||||
disabled = button.disabled,
|
||||
selected = button.selected,
|
||||
isBreadcrumbNew = button.isBreadcrumbNew,
|
||||
warning = button.warning,
|
||||
requiredChunk = button.selectedParam,
|
||||
starterPackUpgrade = button.starterPackUpgrade,
|
||||
unloadMod = button.unloadMod
|
||||
})
|
||||
else
|
||||
table.insert(options, {
|
||||
optionDisplay = button.stringRef,
|
||||
action = button.action,
|
||||
param = button.param,
|
||||
customId = button.customId,
|
||||
isLargeButton = isLargeButton,
|
||||
isLastButtonInGroup = false,
|
||||
disabled = button.disabled,
|
||||
selected = button.selected,
|
||||
isBreadcrumbNew = button.isBreadcrumbNew,
|
||||
warning = button.warning,
|
||||
requiredChunk = button.selectedParam,
|
||||
starterPackUpgrade = button.starterPackUpgrade,
|
||||
unloadMod = button.unloadMod
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
local AddLargeButton = function(controller, options, button, index)
|
||||
AddButton(controller, options, button, true, index)
|
||||
end
|
||||
|
||||
local AddSmallButton = function(controller, options, button, index)
|
||||
AddButton(controller, options, button, false, index)
|
||||
end
|
||||
|
||||
local AddSpacer = function(options, index)
|
||||
if index ~= nil then
|
||||
options[index].isLastButtonInGroup = true
|
||||
elseif 0 < #options then
|
||||
options[#options].isLastButtonInGroup = true
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
AddButton = AddButton,
|
||||
AddLargeButton = AddLargeButton,
|
||||
AddSmallButton = AddSmallButton,
|
||||
AddSpacer = AddSpacer
|
||||
}
|
@ -71,7 +71,7 @@ DataSources.StartMenuGameOptions = ListHelper_SetupDataSource("StartMenuGameOpti
|
||||
end
|
||||
elseif CoD.isZombie then
|
||||
table.insert(options, {models = {displayText = "MENU_RESUMEGAME_CAPS", action = StartMenuGoBack_ListElement}})
|
||||
if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) and (not not (Engine.SessionModeIsMode(CoD.SESSIONMODE_SYSTEMLINK) == true) or Engine.SessionModeIsMode(CoD.SESSIONMODE_OFFLINE) == true) then
|
||||
if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) and (not Engine.SessionModeIsMode(CoD.SESSIONMODE_SYSTEMLINK) or Engine.SessionModeIsMode(CoD.SESSIONMODE_OFFLINE)) then
|
||||
table.insert(options, {models = {displayText = "MENU_RESTART_LEVEL_CAPS", action = RestartGame}})
|
||||
end
|
||||
if Engine.IsLobbyHost(Enum.LobbyType.LOBBY_TYPE_GAME) == true then
|
||||
|
378
data/ui_scripts/server_browser/__init__.lua
Normal file
378
data/ui_scripts/server_browser/__init__.lua
Normal file
@ -0,0 +1,378 @@
|
||||
if Engine.GetCurrentMap() ~= "core_frontend" then
|
||||
return
|
||||
end
|
||||
|
||||
function IsServerBrowserEnabled()
|
||||
return true
|
||||
end
|
||||
|
||||
DataSources.LobbyServer = {
|
||||
prepare = function(controller, list, filter)
|
||||
list.numElementsInList = list.vCount
|
||||
list.controller = controller
|
||||
list.serverBrowserRootModel = Engine.CreateModel(Engine.GetGlobalModel(), "serverBrowser")
|
||||
local serverListCountModel = Engine.GetModel(list.serverBrowserRootModel, "serverListCount")
|
||||
if serverListCountModel then
|
||||
list.serverCount = Engine.GetModelValue(serverListCountModel)
|
||||
else
|
||||
list.serverCount = 0
|
||||
end
|
||||
list.servers = {}
|
||||
local serversModel = Engine.CreateModel(list.serverBrowserRootModel, "servers")
|
||||
for i = 1, list.numElementsInList, 1 do
|
||||
list.servers[i] = {}
|
||||
list.servers[i].root = Engine.CreateModel(serversModel, "server_" .. i)
|
||||
list.servers[i].model = Engine.CreateModel(list.servers[i].root, "model")
|
||||
end
|
||||
list.updateModels = function(controller, list, offset)
|
||||
local serverInfo = Engine.SteamServerBrowser_GetServerInfo(offset)
|
||||
if serverInfo then
|
||||
local SetModelValue = function(model, key, value)
|
||||
local model = Engine.CreateModel(model, key)
|
||||
if model then
|
||||
Engine.SetModelValue(model, value)
|
||||
end
|
||||
end
|
||||
|
||||
local elementIndex = offset % list.numElementsInList + 1
|
||||
local serverModel = list.servers[elementIndex].model
|
||||
SetModelValue(serverModel, "serverIndex", serverInfo.serverIndex)
|
||||
SetModelValue(serverModel, "connectAddr", serverInfo.connectAddr)
|
||||
SetModelValue(serverModel, "ping", serverInfo.ping)
|
||||
SetModelValue(serverModel, "modName", serverInfo.modName)
|
||||
SetModelValue(serverModel, "mapName", serverInfo.map)
|
||||
SetModelValue(serverModel, "desc", serverInfo.desc)
|
||||
-- Changed the client count to be the actual player count
|
||||
local clientCount = serverInfo.playerCount - serverInfo.botCount
|
||||
SetModelValue(serverModel, "clientCount", clientCount)
|
||||
SetModelValue(serverModel, "maxClients", serverInfo.maxPlayers)
|
||||
SetModelValue(serverModel, "passwordProtected", serverInfo.password)
|
||||
SetModelValue(serverModel, "secure", serverInfo.secure)
|
||||
SetModelValue(serverModel, "name", serverInfo.name)
|
||||
SetModelValue(serverModel, "gameType", serverInfo.gametype)
|
||||
SetModelValue(serverModel, "dedicated", serverInfo.dedicated)
|
||||
SetModelValue(serverModel, "ranked", serverInfo.ranked)
|
||||
SetModelValue(serverModel, "hardcore", serverInfo.hardcore)
|
||||
SetModelValue(serverModel, "zombies", serverInfo.zombies)
|
||||
-- Added the bot count
|
||||
SetModelValue(serverModel, "botCount", serverInfo.botCount)
|
||||
return serverModel
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
if list.serverListUpdateSubscription then
|
||||
list:removeSubscription(list.serverListUpdateSubscription)
|
||||
end
|
||||
local serverListUpdateModel = Engine.CreateModel(list.serverBrowserRootModel, "serverListCount")
|
||||
list.serverListUpdateSubscription = list:subscribeToModel(serverListUpdateModel, function(model)
|
||||
list:updateDataSource(false, false)
|
||||
end, false)
|
||||
if list.serverListSortTypeSubscription then
|
||||
list:removeSubscription(list.serverListSortTypeSubscription)
|
||||
end
|
||||
local serverListSortTypeModel = Engine.CreateModel(list.serverBrowserRootModel, "serverListSortType")
|
||||
list.serverListSortTypeSubscription = list:subscribeToModel(serverListSortTypeModel, function(model)
|
||||
list:updateDataSource(false, false)
|
||||
end, false)
|
||||
end,
|
||||
getCount = function(list)
|
||||
return list.serverCount
|
||||
end,
|
||||
getItem = function(controller, list, index)
|
||||
local offset = index - 1
|
||||
return list.updateModels(controller, list, offset)
|
||||
end,
|
||||
cleanup = function(list)
|
||||
if list.serverBrowserRootModel then
|
||||
Engine.UnsubscribeAndFreeModel(list.serverBrowserRootModel)
|
||||
list.serverBrowserRootModel = nil
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
CoD.ServerBrowserRowInternal.new = function(menu, controller)
|
||||
local self = LUI.UIHorizontalList.new({
|
||||
left = 0,
|
||||
top = 0,
|
||||
right = 0,
|
||||
bottom = 0,
|
||||
leftAnchor = true,
|
||||
topAnchor = true,
|
||||
rightAnchor = true,
|
||||
bottomAnchor = true,
|
||||
spacing = 2
|
||||
})
|
||||
self:setAlignment(LUI.Alignment.Left)
|
||||
if PreLoadFunc then
|
||||
PreLoadFunc(self, controller)
|
||||
end
|
||||
self:setUseStencil(false)
|
||||
self:setClass(CoD.ServerBrowserRowInternal)
|
||||
self.id = "ServerBrowserRowInternal"
|
||||
self.soundSet = "default"
|
||||
self:setLeftRight(true, false, 0, 700)
|
||||
self:setTopBottom(true, false, 0, 22)
|
||||
self:makeFocusable()
|
||||
self.onlyChildrenFocusable = true
|
||||
self.anyChildUsesUpdateState = true
|
||||
|
||||
local passwordFlag = CoD.ServerBrowserFlag.new(menu, controller)
|
||||
passwordFlag:setLeftRight(true, false, 0, 28)
|
||||
passwordFlag:setTopBottom(true, true, 0, 0)
|
||||
passwordFlag.icon:setImage(RegisterImage("uie_t7_icon_serverbrowser_protected"))
|
||||
passwordFlag:linkToElementModel(self, nil, false, function(model)
|
||||
passwordFlag:setModel(model, controller)
|
||||
end)
|
||||
passwordFlag:mergeStateConditions({
|
||||
{
|
||||
stateName = "FlagOn",
|
||||
condition = function(menu, element, event)
|
||||
return IsSelfModelValueTrue(element, controller, "passwordProtected")
|
||||
end
|
||||
}
|
||||
})
|
||||
passwordFlag:linkToElementModel(passwordFlag, "passwordProtected", true, function(model)
|
||||
menu:updateElementState(passwordFlag, {
|
||||
name = "model_validation",
|
||||
menu = menu,
|
||||
modelValue = Engine.GetModelValue(model),
|
||||
modelName = "passwordProtected"
|
||||
})
|
||||
end)
|
||||
self:addElement(passwordFlag)
|
||||
self.passwordFlag = passwordFlag
|
||||
|
||||
local dedicatedFlag = CoD.ServerBrowserFlag.new(menu, controller)
|
||||
dedicatedFlag:setLeftRight(true, false, 30, 58)
|
||||
dedicatedFlag:setTopBottom(true, true, 0, 0)
|
||||
dedicatedFlag.icon:setImage(RegisterImage("uie_t7_icon_serverbrowser_dedicated"))
|
||||
dedicatedFlag:linkToElementModel(self, nil, false, function(model)
|
||||
dedicatedFlag:setModel(model, controller)
|
||||
end)
|
||||
dedicatedFlag:mergeStateConditions({
|
||||
{
|
||||
stateName = "FlagOn",
|
||||
condition = function(menu, element, event)
|
||||
return IsSelfModelValueTrue(element, controller, "dedicated")
|
||||
end
|
||||
}
|
||||
})
|
||||
dedicatedFlag:linkToElementModel(dedicatedFlag, "dedicated", true, function(model)
|
||||
menu:updateElementState(dedicatedFlag, {
|
||||
name = "model_validation",
|
||||
menu = menu,
|
||||
modelValue = Engine.GetModelValue(model),
|
||||
modelName = "dedicated"
|
||||
})
|
||||
end)
|
||||
self:addElement(dedicatedFlag)
|
||||
self.dedicatedFlag = dedicatedFlag
|
||||
|
||||
local rankedFlag = CoD.ServerBrowserFlag.new(menu, controller)
|
||||
rankedFlag:setLeftRight(true, false, 60, 88)
|
||||
rankedFlag:setTopBottom(true, true, 0, 0)
|
||||
rankedFlag.icon:setImage(RegisterImage("uie_t7_icon_serverbrowser_ranked"))
|
||||
rankedFlag:linkToElementModel(self, nil, false, function(model)
|
||||
rankedFlag:setModel(model, controller)
|
||||
end)
|
||||
rankedFlag:mergeStateConditions({
|
||||
{
|
||||
stateName = "FlagOn",
|
||||
condition = function(menu, element, event)
|
||||
return IsSelfModelValueTrue(element, controller, "ranked")
|
||||
end
|
||||
}
|
||||
})
|
||||
rankedFlag:linkToElementModel(rankedFlag, "ranked", true, function(model)
|
||||
menu:updateElementState(rankedFlag, {
|
||||
name = "model_validation",
|
||||
menu = menu,
|
||||
modelValue = Engine.GetModelValue(model),
|
||||
modelName = "ranked"
|
||||
})
|
||||
end)
|
||||
self:addElement(rankedFlag)
|
||||
self.rankedFlag = rankedFlag
|
||||
|
||||
local name = CoD.horizontalScrollingTextBox_18pt.new(menu, controller)
|
||||
name:setLeftRight(true, false, 90, 330)
|
||||
name:setTopBottom(true, false, 2, 20)
|
||||
name.textBox:setTTF("fonts/default.ttf")
|
||||
name.textBox:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
name:linkToElementModel(self, "name", true, function(model)
|
||||
local _name = Engine.GetModelValue(model)
|
||||
if _name then
|
||||
name.textBox:setText(Engine.Localize(_name))
|
||||
end
|
||||
end)
|
||||
self:addElement(name)
|
||||
self.name = name
|
||||
|
||||
local spacer = LUI.UIFrame.new(menu, controller, 0, 0, false)
|
||||
spacer:setLeftRight(true, false, 332, 339)
|
||||
spacer:setTopBottom(true, false, 0, 22)
|
||||
spacer:setAlpha(0)
|
||||
self:addElement(spacer)
|
||||
self.spacer = spacer
|
||||
|
||||
local map = CoD.horizontalScrollingTextBox_18pt.new(menu, controller)
|
||||
map:setLeftRight(true, false, 341, 446)
|
||||
map:setTopBottom(true, false, 2, 20)
|
||||
map.textBox:setTTF("fonts/default.ttf")
|
||||
map.textBox:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
map:linkToElementModel(self, "mapName", true, function(model)
|
||||
local mapName = Engine.GetModelValue(model)
|
||||
if mapName then
|
||||
map.textBox:setText(MapNameToLocalizedMapName(mapName))
|
||||
end
|
||||
end)
|
||||
self:addElement(map)
|
||||
self.map = map
|
||||
|
||||
local hardcoreFlag = CoD.ServerBrowserFlag.new(menu, controller)
|
||||
hardcoreFlag:setLeftRight(true, false, 448, 470)
|
||||
hardcoreFlag:setTopBottom(true, true, 0, 0)
|
||||
hardcoreFlag.icon:setImage(RegisterImage("uie_t7_icon_serverbrowser_skull"))
|
||||
hardcoreFlag:linkToElementModel(self, nil, false, function(model)
|
||||
hardcoreFlag:setModel(model, controller)
|
||||
end)
|
||||
hardcoreFlag:mergeStateConditions({
|
||||
{
|
||||
stateName = "FlagOn",
|
||||
condition = function(menu, element, event)
|
||||
return IsSelfModelValueTrue(element, controller, "hardcore")
|
||||
end
|
||||
}
|
||||
})
|
||||
hardcoreFlag:linkToElementModel(hardcoreFlag, "hardcore", true, function(model)
|
||||
menu:updateElementState(hardcoreFlag, {
|
||||
name = "model_validation",
|
||||
menu = menu,
|
||||
modelValue = Engine.GetModelValue(model),
|
||||
modelName = "hardcore"
|
||||
})
|
||||
end)
|
||||
self:addElement(hardcoreFlag)
|
||||
self.hardcoreFlag = hardcoreFlag
|
||||
|
||||
local gametype = LUI.UIText.new()
|
||||
gametype:setLeftRight(true, false, 472, 576)
|
||||
gametype:setTopBottom(true, false, 2, 20)
|
||||
gametype:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
gametype:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
gametype:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
gametype:linkToElementModel(self, "gameType", true, function(model)
|
||||
local gameType = Engine.GetModelValue(model)
|
||||
if gameType then
|
||||
gametype:setText(Engine.Localize(GetGameTypeDisplayString(gameType)))
|
||||
end
|
||||
end)
|
||||
self:addElement(gametype)
|
||||
self.gametype = gametype
|
||||
|
||||
local playerCount = LUI.UIText.new()
|
||||
playerCount:setLeftRight(true, false, 593, 613)
|
||||
playerCount:setTopBottom(true, false, 2, 20)
|
||||
playerCount:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
playerCount:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_RIGHT)
|
||||
playerCount:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
playerCount:linkToElementModel(self, "clientCount", true, function(model)
|
||||
local clientCount = Engine.GetModelValue(model)
|
||||
if clientCount then
|
||||
playerCount:setText(Engine.Localize(clientCount))
|
||||
end
|
||||
end)
|
||||
self:addElement(playerCount)
|
||||
self.playerCount = playerCount
|
||||
|
||||
local slash = LUI.UIText.new()
|
||||
slash:setLeftRight(true, false, 615, 624)
|
||||
slash:setTopBottom(true, false, 2, 20)
|
||||
slash:setText(Engine.Localize("/"))
|
||||
slash:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
slash:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
slash:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
self:addElement(slash)
|
||||
self.slash = slash
|
||||
|
||||
local maxPlayers = LUI.UIText.new()
|
||||
maxPlayers:setLeftRight(true, false, 626, 645)
|
||||
maxPlayers:setTopBottom(true, false, 2, 20)
|
||||
maxPlayers:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
maxPlayers:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
maxPlayers:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
maxPlayers:linkToElementModel(self, "maxClients", true, function(model)
|
||||
local maxClients = Engine.GetModelValue(model)
|
||||
if maxClients then
|
||||
maxPlayers:setText(Engine.Localize(maxClients))
|
||||
end
|
||||
end)
|
||||
self:addElement(maxPlayers)
|
||||
self.maxPlayers = maxPlayers
|
||||
|
||||
local botCount = LUI.UIText.new()
|
||||
botCount:setLeftRight(true, false, 637, 659)
|
||||
botCount:setTopBottom(true, false, 2, 20)
|
||||
botCount:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
botCount:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_LEFT)
|
||||
botCount:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
botCount:linkToElementModel(self, "botCount", true, function(model)
|
||||
local _botCount = Engine.GetModelValue(model)
|
||||
if _botCount then
|
||||
botCount:setText("[" .. Engine.Localize(_botCount) .. "]")
|
||||
end
|
||||
end)
|
||||
botCount:linkToElementModel(self, "zombies", true, function(model)
|
||||
local zombies = Engine.GetModelValue(model)
|
||||
if zombies ~= nil then
|
||||
botCount:setAlpha(zombies and 0 or 1)
|
||||
end
|
||||
end)
|
||||
self:addElement(botCount)
|
||||
self.botCount = botCount
|
||||
|
||||
local ping = LUI.UIText.new()
|
||||
ping:setLeftRight(true, false, 661, 699.37)
|
||||
ping:setTopBottom(true, false, 2, 20)
|
||||
ping:setTTF("fonts/RefrigeratorDeluxe-Regular.ttf")
|
||||
ping:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_CENTER)
|
||||
ping:setAlignment(Enum.LUIAlignment.LUI_ALIGNMENT_TOP)
|
||||
ping:linkToElementModel(self, "ping", true, function(model)
|
||||
local _ping = Engine.GetModelValue(model)
|
||||
if _ping then
|
||||
ping:setText(Engine.Localize(_ping))
|
||||
end
|
||||
end)
|
||||
self:addElement(ping)
|
||||
self.ping = ping
|
||||
|
||||
spacer.id = "spacer"
|
||||
self:registerEventHandler("gain_focus", function(self, event)
|
||||
if self.m_focusable and self.spacer:processEvent(event) then
|
||||
return true
|
||||
else
|
||||
return LUI.UIElement.gainFocus(self, event)
|
||||
end
|
||||
end)
|
||||
LUI.OverrideFunction_CallOriginalSecond(self, "close", function(element)
|
||||
element.passwordFlag:close()
|
||||
element.dedicatedFlag:close()
|
||||
element.rankedFlag:close()
|
||||
element.name:close()
|
||||
element.map:close()
|
||||
element.hardcoreFlag:close()
|
||||
element.gametype:close()
|
||||
element.playerCount:close()
|
||||
element.maxPlayers:close()
|
||||
element.botCount:close()
|
||||
element.ping:close()
|
||||
end)
|
||||
|
||||
if PostLoadFunc then
|
||||
PostLoadFunc(self, controller, menu)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
@ -1,3 +0,0 @@
|
||||
function IsServerBrowserEnabled()
|
||||
return true
|
||||
end
|
@ -2,23 +2,26 @@ if Engine.GetCurrentMap() ~= "core_frontend" then
|
||||
return
|
||||
end
|
||||
|
||||
DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", function ( controller )
|
||||
DataSources.MPStatsSettings = DataSourceHelpers.ListSetup("MPStatsSettings", function(controller)
|
||||
local optionsTable = {}
|
||||
|
||||
local updateDvar = function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4)
|
||||
local oldValue = Engine.DvarInt( nil, dvarName )
|
||||
local oldValue = Engine.DvarInt(nil, dvarName)
|
||||
local newValue = f1_arg1.value
|
||||
UpdateInfoModels( f1_arg1 )
|
||||
UpdateInfoModels(f1_arg1)
|
||||
if oldValue == newValue then
|
||||
return
|
||||
end
|
||||
Engine.SetDvar( dvarName, f1_arg1.value )
|
||||
Engine.SetDvar(dvarName, f1_arg1.value)
|
||||
if dvarName == "cg_unlockall_loot" then
|
||||
Engine.SetDvar( "ui_enableAllHeroes", f1_arg1.value )
|
||||
Engine.SetDvar("ui_enableAllHeroes", f1_arg1.value)
|
||||
end
|
||||
end
|
||||
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock All Loot", "Whether loot should be locked based on the player's stats or always unlocked.", "MPStatsSettings_unlock_loot", "cg_unlockall_loot", {
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Loot",
|
||||
"Unlocks all Black Market loot.", "MPStatsSettings_unlock_loot",
|
||||
"cg_unlockall_loot", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -28,9 +31,12 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
}, nil, updateDvar))
|
||||
if Engine.CurrentSessionMode() == Enum.eModes.MODE_MULTIPLAYER then
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock All Purchases", "All items that need to be purchased with unlock tokens are unlocked.", "MPStatsSettings_purchase_all", "cg_unlockall_purchases", {
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Purchases",
|
||||
"All items that need to be purchased with unlock tokens are unlocked.", "MPStatsSettings_purchase_all",
|
||||
"cg_unlockall_purchases", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -40,9 +46,26 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
}, nil, updateDvar))
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Class Slots",
|
||||
"Unlocks all create-a-class slots and sets.", "MPStatsSettings_unlockall_cac_slots",
|
||||
"cg_unlockall_cac_slots", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
default = true
|
||||
},
|
||||
{
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar))
|
||||
end
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock All Class Slots", "Unlock all create-a-class slots and sets.", "MPStatsSettings_unlockall_cac_slots", "cg_unlockall_cac_slots", {
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Attachments",
|
||||
"All attachments on weapons are unlocked.",
|
||||
"MPStatsSettings_unlockall_attachments", "cg_unlockall_attachments", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -52,8 +75,11 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock All Attachments", "All attachments on weapons are unlocked.", "MPStatsSettings_unlockall_attachments", "cg_unlockall_attachments", {
|
||||
}, nil, updateDvar))
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock all Camos and Reticles",
|
||||
"All camos and reticles on weapons are unlocked.", "MPStatsSettings_unlockall_camos_and_reticles",
|
||||
"cg_unlockall_camos_and_reticles", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -63,8 +89,10 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock all Camos and Reticles", "All camos and reticles on weapons are unlocked.", "MPStatsSettings_unlockall_camos_and_reticles", "cg_unlockall_camos_and_reticles", {
|
||||
}, nil, updateDvar))
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock all Calling Cards", "All calling cards are unlocked.",
|
||||
"MPStatsSettings_unlockall_calling_cards", "cg_unlockall_calling_cards", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -74,8 +102,11 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock all Calling Cards", "All calling cards are unlocked.", "MPStatsSettings_unlockall_calling_cards", "cg_unlockall_calling_cards", {
|
||||
}, nil, updateDvar))
|
||||
table.insert(optionsTable,
|
||||
CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock all Specialists Outfits",
|
||||
"All specialists outfits are unlocked.", "MPStatsSettings_unlockall_specialists_outfits",
|
||||
"cg_unlockall_specialists_outfits", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
@ -85,18 +116,7 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
table.insert( optionsTable, CoD.OptionsUtility.CreateDvarSettings( controller, "Unlock all Specialists Outfits", "All specialists outfits are unlocked.", "MPStatsSettings_unlockall_specialists_outfits", "cg_unlockall_specialists_outfits", {
|
||||
{
|
||||
option = "MENU_DISABLED",
|
||||
value = 0,
|
||||
default = true
|
||||
},
|
||||
{
|
||||
option = "MENU_ENABLED",
|
||||
value = 1
|
||||
},
|
||||
}, nil, updateDvar ))
|
||||
}, nil, updateDvar))
|
||||
|
||||
local rankLevels = {}
|
||||
if Engine.CurrentSessionMode() == Enum.eModes.MODE_MULTIPLAYER then
|
||||
@ -106,9 +126,9 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
end
|
||||
local rankObjs = {}
|
||||
local hasDefault = false
|
||||
local currentRank = CoD.BlackMarketUtility.GetCurrentRank( controller ) + 1
|
||||
local currentRank = CoD.BlackMarketUtility.GetCurrentRank(controller) + 1
|
||||
for index, value in ipairs(rankLevels) do
|
||||
table.insert( rankObjs, {
|
||||
table.insert(rankObjs, {
|
||||
name = value,
|
||||
value = value - 1,
|
||||
default = value == currentRank,
|
||||
@ -121,7 +141,7 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
end
|
||||
|
||||
if not hasDefault then
|
||||
table.insert( rankObjs, {
|
||||
table.insert(rankObjs, {
|
||||
name = currentRank,
|
||||
value = currentRank - 1,
|
||||
default = true,
|
||||
@ -132,23 +152,23 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
|
||||
local prestigeTable = {}
|
||||
for i = 0, 10 do
|
||||
table.insert( prestigeTable, {
|
||||
table.insert(prestigeTable, {
|
||||
name = i == 0 and "None" or i,
|
||||
value = i,
|
||||
default = i == CoD.PrestigeUtility.GetCurrentPLevel( controller ),
|
||||
default = i == CoD.PrestigeUtility.GetCurrentPLevel(controller),
|
||||
title = "Prestige",
|
||||
desc = ""
|
||||
})
|
||||
end
|
||||
|
||||
local createSettingsDatasource = function ( controller, datasourceName, optionsTable, currentValue, loopEdges, action )
|
||||
local createSettingsDatasource = function(controller, datasourceName, optionsTable, currentValue, loopEdges, action)
|
||||
if currentValue == nil then
|
||||
currentValue = 0
|
||||
end
|
||||
DataSources[datasourceName] = DataSourceHelpers.ListSetup( datasourceName, function ( f47_arg0 )
|
||||
DataSources[datasourceName] = DataSourceHelpers.ListSetup(datasourceName, function(f47_arg0)
|
||||
local f47_local0 = {}
|
||||
for f47_local4, f47_local5 in ipairs( optionsTable ) do
|
||||
table.insert( f47_local0, {
|
||||
for f47_local4, f47_local5 in ipairs(optionsTable) do
|
||||
table.insert(f47_local0, {
|
||||
models = {
|
||||
text = optionsTable[f47_local4].name
|
||||
},
|
||||
@ -161,26 +181,27 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
action = action,
|
||||
selectIndex = optionsTable[f47_local4].value == currentValue,
|
||||
loopEdges = loopEdges,
|
||||
showChangeIndicator = function ( f48_arg0, f48_arg1, f48_arg2 )
|
||||
showChangeIndicator = function(f48_arg0, f48_arg1, f48_arg2)
|
||||
return f48_arg0.default ~= true
|
||||
end
|
||||
}
|
||||
} )
|
||||
})
|
||||
end
|
||||
f47_local0[1].properties.first = true
|
||||
f47_local0[#optionsTable].properties.last = true
|
||||
return f47_local0
|
||||
end, nil, nil, nil )
|
||||
end, nil, nil, nil)
|
||||
return datasourceName
|
||||
end
|
||||
|
||||
table.insert( optionsTable, {
|
||||
table.insert(optionsTable, {
|
||||
models = {
|
||||
name = "Rank Level",
|
||||
desc = "",
|
||||
image = nil,
|
||||
optionsDatasource = createSettingsDatasource( controller, "MPStatsSettings_rank_level", rankObjs, CoD.BlackMarketUtility.GetCurrentRank( controller ), false, function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4)
|
||||
UpdateInfoModels( f1_arg1 )
|
||||
optionsDatasource = createSettingsDatasource(controller, "MPStatsSettings_rank_level", rankObjs,
|
||||
CoD.BlackMarketUtility.GetCurrentRank(controller), false, function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4)
|
||||
UpdateInfoModels(f1_arg1)
|
||||
local rankTable = nil
|
||||
if Engine.CurrentSessionMode() == Enum.eModes.MODE_MULTIPLAYER then
|
||||
rankTable = "gamedata/tables/mp/mp_ranktable.csv"
|
||||
@ -194,29 +215,32 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
end
|
||||
Engine.ExecNow(f1_arg0, "statsetbyname rankxp " .. maxXp - 1)
|
||||
Engine.ExecNow(f1_arg0, "statsetbyname rank " .. f1_arg1.value)
|
||||
Engine.Exec( f1_arg0, "uploadstats " .. tostring( Engine.CurrentSessionMode() ) )
|
||||
end )
|
||||
Engine.Exec(f1_arg0, "uploadstats " .. tostring(Engine.CurrentSessionMode()))
|
||||
end)
|
||||
},
|
||||
properties = {
|
||||
revert = function ( f50_arg0 ) end
|
||||
revert = function(f50_arg0)
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
table.insert( optionsTable, {
|
||||
table.insert(optionsTable, {
|
||||
models = {
|
||||
name = "Prestige",
|
||||
desc = "",
|
||||
image = nil,
|
||||
optionsDatasource = createSettingsDatasource( controller, "MPStatsSettings_rank_prestige", prestigeTable, CoD.PrestigeUtility.GetCurrentPLevel( controller ), false, function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4)
|
||||
UpdateInfoModels( f1_arg1 )
|
||||
optionsDatasource = createSettingsDatasource(controller, "MPStatsSettings_rank_prestige", prestigeTable,
|
||||
CoD.PrestigeUtility.GetCurrentPLevel(controller), false, function(f1_arg0, f1_arg1, f1_arg2, dvarName, f1_arg4)
|
||||
UpdateInfoModels(f1_arg1)
|
||||
local newPrestige = f1_arg1.value
|
||||
Engine.ExecNow(f1_arg0, "statsetbyname plevel " .. newPrestige)
|
||||
Engine.ExecNow(f1_arg0, "statsetbyname hasprestiged " .. (newPrestige > 0 and 1 or 0))
|
||||
Engine.Exec( f1_arg0, "uploadstats " .. tostring( Engine.CurrentSessionMode() ) )
|
||||
end )
|
||||
Engine.Exec(f1_arg0, "uploadstats " .. tostring(Engine.CurrentSessionMode()))
|
||||
end)
|
||||
},
|
||||
properties = {
|
||||
revert = function ( f50_arg0 ) end
|
||||
revert = function(f50_arg0)
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
@ -224,355 +248,80 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup( "MPStatsSettings", fu
|
||||
end)
|
||||
|
||||
if Dvar.cg_unlockall_loot:get() == true then
|
||||
Engine.SetDvar( "ui_enableAllHeroes", 1 )
|
||||
Engine.SetDvar("ui_enableAllHeroes", 1)
|
||||
end
|
||||
|
||||
LUI.createMenu.BoiiiStatsMenu = function ( controller )
|
||||
local self = CoD.Menu.NewForUIEditor( "BoiiiStatsMenu" )
|
||||
LUI.createMenu.BoiiiStatsMenu = function(controller)
|
||||
local self = CoD.Menu.NewForUIEditor("BoiiiStatsMenu")
|
||||
if PreLoadFunc then
|
||||
PreLoadFunc( self, controller )
|
||||
PreLoadFunc(self, controller)
|
||||
end
|
||||
self.soundSet = "ChooseDecal"
|
||||
self:setOwner( controller )
|
||||
self:setLeftRight( true, true, 0, 0 )
|
||||
self:setTopBottom( true, true, 0, 0 )
|
||||
self:playSound( "menu_open", controller )
|
||||
self.buttonModel = Engine.CreateModel( Engine.GetModelForController( controller ), "BoiiiStatsMenu.buttonPrompts" )
|
||||
self:setOwner(controller)
|
||||
self:setLeftRight(true, true, 0, 0)
|
||||
self:setTopBottom(true, true, 0, 0)
|
||||
self:playSound("menu_open", controller)
|
||||
self.buttonModel = Engine.CreateModel(Engine.GetModelForController(controller), "BoiiiStatsMenu.buttonPrompts")
|
||||
self.anyChildUsesUpdateState = true
|
||||
|
||||
local GameSettingsBackground = CoD.GameSettings_Background.new( self, controller )
|
||||
GameSettingsBackground:setLeftRight( true, true, 0, 0 )
|
||||
GameSettingsBackground:setTopBottom( true, true, 0, 0 )
|
||||
GameSettingsBackground.MenuFrame.titleLabel:setText( Engine.Localize( "STATS SETTINGS" ) )
|
||||
GameSettingsBackground.MenuFrame.cac3dTitleIntermediary0.FE3dTitleContainer0.MenuTitle.TextBox1.Label0:setText( Engine.Localize( "STATS SETTINGS" ) )
|
||||
GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeInfo:setAlpha( 0 )
|
||||
GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeName:setAlpha( 0 )
|
||||
self:addElement( GameSettingsBackground )
|
||||
local GameSettingsBackground = CoD.GameSettings_Background.new(self, controller)
|
||||
GameSettingsBackground:setLeftRight(true, true, 0, 0)
|
||||
GameSettingsBackground:setTopBottom(true, true, 0, 0)
|
||||
GameSettingsBackground.MenuFrame.titleLabel:setText(Engine.Localize("STATS SETTINGS"))
|
||||
GameSettingsBackground.MenuFrame.cac3dTitleIntermediary0.FE3dTitleContainer0.MenuTitle.TextBox1.Label0:setText(Engine
|
||||
.Localize("STATS SETTINGS"))
|
||||
GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeInfo:setAlpha(0)
|
||||
GameSettingsBackground.GameSettingsSelectedItemInfo.GameModeName:setAlpha(0)
|
||||
self:addElement(GameSettingsBackground)
|
||||
self.GameSettingsBackground = GameSettingsBackground
|
||||
|
||||
local Options = CoD.Competitive_SettingsList.new( self, controller )
|
||||
Options:setLeftRight( true, false, 26, 741 )
|
||||
Options:setTopBottom( true, false, 135, 720 )
|
||||
Options.Title.DescTitle:setText( Engine.Localize( "Stats" ) )
|
||||
Options.ButtonList:setVerticalCount( 15 )
|
||||
Options.ButtonList:setDataSource( "MPStatsSettings" )
|
||||
self:addElement( Options )
|
||||
local Options = CoD.Competitive_SettingsList.new(self, controller)
|
||||
Options:setLeftRight(true, false, 26, 741)
|
||||
Options:setTopBottom(true, false, 135, 720)
|
||||
Options.Title.DescTitle:setText(Engine.Localize("Stats"))
|
||||
Options.ButtonList:setVerticalCount(15)
|
||||
Options.ButtonList:setDataSource("MPStatsSettings")
|
||||
self:addElement(Options)
|
||||
self.Options = Options
|
||||
|
||||
self:AddButtonCallbackFunction( self, controller, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, nil, function ( element, menu, controller, model )
|
||||
GoBack( self, controller )
|
||||
SetPerControllerTableProperty( controller, "disableGameSettingsOptions", nil )
|
||||
self:AddButtonCallbackFunction(self, controller, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, nil,
|
||||
function(element, menu, controller, model)
|
||||
GoBack(self, controller)
|
||||
SetPerControllerTableProperty(controller, "disableGameSettingsOptions", nil)
|
||||
return true
|
||||
end, function ( element, menu, controller )
|
||||
CoD.Menu.SetButtonLabel( menu, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, "MENU_BACK" )
|
||||
end, function(element, menu, controller)
|
||||
CoD.Menu.SetButtonLabel(menu, Enum.LUIButton.LUI_KEY_XBB_PSCIRCLE, "MENU_BACK")
|
||||
return true
|
||||
end, false )
|
||||
end, false)
|
||||
|
||||
GameSettingsBackground.MenuFrame:setModel( self.buttonModel, controller )
|
||||
GameSettingsBackground.MenuFrame:setModel(self.buttonModel, controller)
|
||||
Options.id = "Options"
|
||||
|
||||
self:processEvent( {
|
||||
self:processEvent({
|
||||
name = "menu_loaded",
|
||||
controller = controller
|
||||
} )
|
||||
self:processEvent( {
|
||||
})
|
||||
self:processEvent({
|
||||
name = "update_state",
|
||||
menu = self
|
||||
} )
|
||||
})
|
||||
if not self:restoreState() then
|
||||
self.Options:processEvent( {
|
||||
self.Options:processEvent({
|
||||
name = "gain_focus",
|
||||
controller = controller
|
||||
} )
|
||||
})
|
||||
end
|
||||
|
||||
LUI.OverrideFunction_CallOriginalSecond( self, "close", function ( element )
|
||||
LUI.OverrideFunction_CallOriginalSecond(self, "close", function(element)
|
||||
element.GameSettingsBackground:close()
|
||||
element.Options:close()
|
||||
Engine.UnsubscribeAndFreeModel( Engine.GetModel( Engine.GetModelForController( controller ), "BoiiiStatsMenu.buttonPrompts" ) )
|
||||
end )
|
||||
Engine.UnsubscribeAndFreeModel(Engine.GetModel(Engine.GetModelForController(controller),
|
||||
"BoiiiStatsMenu.buttonPrompts"))
|
||||
end)
|
||||
|
||||
if PostLoadFunc then
|
||||
PostLoadFunc( self, controller )
|
||||
PostLoadFunc(self, controller)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
CoD.LobbyButtons.MP_STATS = {
|
||||
stringRef = "STATS",
|
||||
action = function ( self, element, controller, param, menu )
|
||||
SetPerControllerTableProperty( controller, "disableGameSettingsOptions", true )
|
||||
OpenPopup( menu, "BoiiiStatsMenu", controller )
|
||||
end,
|
||||
customId = "btnMPStats"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.MP_START_GAME = {
|
||||
stringRef = "MENU_START_GAME_CAPS",
|
||||
action = function ( self, element, controller, param, menu )
|
||||
--Engine.SetDvar( "bot_difficulty", 3 )
|
||||
Engine.SetDvar( "party_minplayers", 1 )
|
||||
Engine.Exec( nil, "launchgame" )
|
||||
end,
|
||||
customId = "btnStartGame"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.SETTING_UP_BOTS = {
|
||||
stringRef = "MENU_SETUP_BOTS_CAPS",
|
||||
action = function ( self, element, controller, param, menu )
|
||||
SetPerControllerTableProperty( controller, "disableGameSettingsOptions", true )
|
||||
OpenPopup( menu, "GameSettings_Bots", controller )
|
||||
end,
|
||||
customId = "btnSettingUpBots"
|
||||
}
|
||||
|
||||
CoD.LobbyButtons.MP_CUSTOM_SETUP_GAME = {
|
||||
stringRef = "MPUI_SETUP_GAME_CAPS",
|
||||
action = OpenSetupGameMP,
|
||||
customId = "btnSetupGame",
|
||||
}
|
||||
|
||||
local IsGamescomDemo = function ()
|
||||
return Dvar.ui_execdemo_gamescom:get()
|
||||
end
|
||||
|
||||
local IsBetaDemo = function ()
|
||||
return Dvar.ui_execdemo_beta:get()
|
||||
end
|
||||
|
||||
local SetButtonState = function ( button, state )
|
||||
if state == nil then
|
||||
return
|
||||
elseif state == CoD.LobbyButtons.DISABLED then
|
||||
button.disabled = true
|
||||
elseif state == CoD.LobbyButtons.HIDDEN then
|
||||
button.hidden = true
|
||||
end
|
||||
end
|
||||
|
||||
local AddButton = function ( controller, options, button, isLargeButton )
|
||||
button.disabled = false
|
||||
button.hidden = false
|
||||
button.selected = false
|
||||
button.warning = false
|
||||
if button.defaultState ~= nil then
|
||||
if button.defaultState == CoD.LobbyButtons.DISABLED then
|
||||
button.disabled = true
|
||||
elseif button.defaultState == CoD.LobbyButtons.HIDDEN then
|
||||
button.hidden = true
|
||||
end
|
||||
end
|
||||
if button.disabledFunc ~= nil then
|
||||
button.disabled = button.disabledFunc( controller )
|
||||
end
|
||||
if button.visibleFunc ~= nil then
|
||||
button.hidden = not button.visibleFunc( controller )
|
||||
end
|
||||
if IsBetaDemo() then
|
||||
SetButtonState( button, button.demo_beta )
|
||||
elseif IsGamescomDemo() then
|
||||
SetButtonState( button, button.demo_gamescom )
|
||||
end
|
||||
if button.hidden then
|
||||
return
|
||||
end
|
||||
local lobbyNav = LobbyData.GetLobbyNav()
|
||||
if button.selectedFunc ~= nil then
|
||||
button.selected = button.selectedFunc( button.selectedParam )
|
||||
elseif CoD.LobbyMenus.History[lobbyNav] ~= nil then
|
||||
button.selected = CoD.LobbyMenus.History[lobbyNav] == button.customId
|
||||
end
|
||||
if button.newBreadcrumbFunc then
|
||||
local f8_local1 = button.newBreadcrumbFunc
|
||||
if type( f8_local1 ) == "string" then
|
||||
f8_local1 = LUI.getTableFromPath( f8_local1 )
|
||||
end
|
||||
if f8_local1 then
|
||||
button.isBreadcrumbNew = f8_local1( controller )
|
||||
end
|
||||
end
|
||||
if button.warningFunc ~= nil then
|
||||
button.warning = button.warningFunc( controller )
|
||||
end
|
||||
if button.starterPack == CoD.LobbyButtons.STARTERPACK_UPGRADE then
|
||||
button.starterPackUpgrade = true
|
||||
if IsStarterPack() then
|
||||
button.disabled = false
|
||||
end
|
||||
end
|
||||
table.insert( options, {
|
||||
optionDisplay = button.stringRef,
|
||||
action = button.action,
|
||||
param = button.param,
|
||||
customId = button.customId,
|
||||
isLargeButton = isLargeButton,
|
||||
isLastButtonInGroup = false,
|
||||
disabled = button.disabled,
|
||||
selected = button.selected,
|
||||
isBreadcrumbNew = button.isBreadcrumbNew,
|
||||
warning = button.warning,
|
||||
requiredChunk = button.selectedParam,
|
||||
starterPackUpgrade = button.starterPackUpgrade,
|
||||
unloadMod = button.unloadMod
|
||||
} )
|
||||
end
|
||||
|
||||
local AddLargeButton = function ( controller, options, button )
|
||||
AddButton( controller, options, button, true )
|
||||
end
|
||||
|
||||
local AddSmallButton = function ( controller, options, button )
|
||||
AddButton( controller, options, button, false )
|
||||
end
|
||||
|
||||
local AddSpacer = function ( options )
|
||||
if 0 < #options then
|
||||
options[#options].isLastButtonInGroup = true
|
||||
end
|
||||
end
|
||||
|
||||
local MapVote = 0
|
||||
|
||||
CoD.LobbyMenus.MPButtonsOnline = function ( f26_arg0, f26_arg1, f26_arg2 )
|
||||
if f26_arg2 == 1 then
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_FIND_MATCH )
|
||||
AddSpacer( f26_arg1 )
|
||||
end
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_CAC_NO_WARNING )
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_SPECIALISTS_NO_WARNING )
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_SCORESTREAKS )
|
||||
if (Dvar.ui_execdemo_beta:get() or IsStarterPack()) and IsStoreAvailable() then
|
||||
if CoD.isPC then
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.STEAM_STORE )
|
||||
else
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.STORE )
|
||||
end
|
||||
end
|
||||
if Engine.DvarBool( nil, "inventory_test_button_visible" ) then
|
||||
AddLargeButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_INVENTORY_TEST )
|
||||
end
|
||||
AddSpacer( f26_arg1 )
|
||||
if not DisableBlackMarket() then
|
||||
AddSmallButton( f26_arg0, f26_arg1, CoD.LobbyButtons.BLACK_MARKET )
|
||||
end
|
||||
AddSpacer( f26_arg1 )
|
||||
AddSmallButton( f26_arg0, f26_arg1, CoD.LobbyButtons.MP_STATS )
|
||||
MapVote = 1
|
||||
end
|
||||
|
||||
CoD.LobbyMenus.MPButtonsOnlinePublic = function ( f27_arg0, f27_arg1, f27_arg2 )
|
||||
if MapVote == 1 then
|
||||
Engine.Exec(nil, "LobbyStopDemo") -- Enable map vote at start lobby
|
||||
MapVote = 0
|
||||
end
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_START_GAME ) --Launch match button
|
||||
AddSpacer( f27_arg1 )
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_CAC )
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_SPECIALISTS )
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_SCORESTREAKS )
|
||||
if Engine.DvarBool( nil, "inventory_test_button_visible" ) then
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_INVENTORY_TEST )
|
||||
end
|
||||
--[[local f27_local0 = Engine.GetPlaylistInfoByID( Engine.GetPlaylistID() )
|
||||
if f27_local0 then
|
||||
local f27_local1 = f27_local0.playlist.category
|
||||
if f27_local1 == Engine.GetPlaylistCategoryIdByName( "core" ) or f27_local1 == Engine.GetPlaylistCategoryIdByName( "hardcore" ) then
|
||||
AddSpacer( f27_arg1 )
|
||||
AddSmallButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_PUBLIC_LOBBY_LEADERBOARD )
|
||||
end
|
||||
end
|
||||
]] if not DisableBlackMarket() then
|
||||
AddSpacer( f27_arg1 )
|
||||
AddLargeButton( f27_arg0, f27_arg1, CoD.LobbyButtons.BLACK_MARKET )
|
||||
end
|
||||
AddSpacer( f27_arg1 )
|
||||
AddSmallButton( f27_arg0, f27_arg1, CoD.LobbyButtons.MP_CUSTOM_SETUP_GAME ) --Setup game in public lobby
|
||||
end
|
||||
|
||||
CoD.LobbyMenus.MPButtonsArenaGame = function ( f31_arg0, f31_arg1, f31_arg2 )
|
||||
AddLargeButton( f31_arg0, f31_arg1, CoD.LobbyButtons.MP_START_GAME ) --Launch match button
|
||||
AddSpacer( f31_arg1 )
|
||||
AddLargeButton( f31_arg0, f31_arg1, CoD.LobbyButtons.MP_CAC )
|
||||
AddLargeButton( f31_arg0, f31_arg1, CoD.LobbyButtons.MP_SPECIALISTS )
|
||||
AddLargeButton( f31_arg0, f31_arg1, CoD.LobbyButtons.MP_SCORESTREAKS )
|
||||
if not DisableBlackMarket() then
|
||||
AddSpacer( f31_arg1 )
|
||||
AddLargeButton( f31_arg0, f31_arg1, CoD.LobbyButtons.BLACK_MARKET )
|
||||
end
|
||||
AddSpacer( f31_arg1 )
|
||||
AddSmallButton( f31_arg0, f31_arg1, CoD.LobbyButtons.SETTING_UP_BOTS ) --Bot setting button in public lobby
|
||||
end
|
||||
|
||||
CoD.LobbyMenus.ZMButtonsOnline = function ( f33_arg0, f33_arg1, f33_arg2 )
|
||||
if IsStarterPack() then
|
||||
AddSmallButton( f33_arg0, f33_arg1, CoD.LobbyButtons.QUIT )
|
||||
return
|
||||
elseif f33_arg2 == 1 then
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_SOLO_GAME )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_FIND_MATCH )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_CUSTOM_GAMES )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.THEATER_ZM )
|
||||
AddSpacer( f33_arg1 )
|
||||
end
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_BUBBLEGUM_BUFFS )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_MEGACHEW_FACTORY )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_GOBBLEGUM_RECIPES )
|
||||
AddLargeButton( f33_arg0, f33_arg1, CoD.LobbyButtons.ZM_BUILD_KITS )
|
||||
AddSpacer( f33_arg1 )
|
||||
AddSmallButton( f33_arg0, f33_arg1, CoD.LobbyButtons.MP_STATS )
|
||||
end
|
||||
|
||||
local targetButtons = {
|
||||
[LobbyData.UITargets.UI_MAIN.id] = CoD.LobbyMenus.ModeSelect,
|
||||
[LobbyData.UITargets.UI_MODESELECT.id] = CoD.LobbyMenus.ModeSelect,
|
||||
[LobbyData.UITargets.UI_CPLOBBYLANGAME.id] = CoD.LobbyMenus.CPButtonsLAN,
|
||||
[LobbyData.UITargets.UI_CPLOBBYLANCUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsLANCUSTOM,
|
||||
[LobbyData.UITargets.UI_CPLOBBYONLINE.id] = CoD.LobbyMenus.CPButtonsOnline,
|
||||
[LobbyData.UITargets.UI_CPLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.CPButtonsPublicGame,
|
||||
[LobbyData.UITargets.UI_CPLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsCustomGame,
|
||||
[LobbyData.UITargets.UI_CP2LOBBYLANGAME.id] = CoD.LobbyMenus.CPZMButtonsLAN,
|
||||
[LobbyData.UITargets.UI_CP2LOBBYLANCUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsLANCUSTOM,
|
||||
[LobbyData.UITargets.UI_CP2LOBBYONLINE.id] = CoD.LobbyMenus.CPZMButtonsOnline,
|
||||
[LobbyData.UITargets.UI_CP2LOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.CPZMButtonsPublicGame,
|
||||
[LobbyData.UITargets.UI_CP2LOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.CPButtonsCustomGame,
|
||||
[LobbyData.UITargets.UI_DOALOBBYLANGAME.id] = CoD.LobbyMenus.DOAButtonsLAN,
|
||||
[LobbyData.UITargets.UI_DOALOBBYONLINE.id] = CoD.LobbyMenus.DOAButtonsOnline,
|
||||
[LobbyData.UITargets.UI_DOALOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.DOAButtonsPublicGame,
|
||||
[LobbyData.UITargets.UI_MPLOBBYLANGAME.id] = CoD.LobbyMenus.MPButtonsLAN,
|
||||
[LobbyData.UITargets.UI_MPLOBBYMAIN.id] = CoD.LobbyMenus.MPButtonsMain,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINE.id] = CoD.LobbyMenus.MPButtonsOnline,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.MPButtonsOnlinePublic,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINEMODGAME.id] = CoD.LobbyMenus.MPButtonsModGame,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.MPButtonsCustomGame,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINEARENA.id] = CoD.LobbyMenus.MPButtonsArena,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINEARENAGAME.id] = CoD.LobbyMenus.MPButtonsArenaGame,
|
||||
[LobbyData.UITargets.UI_FRLOBBYONLINEGAME.id] = CoD.LobbyMenus.FRButtonsOnlineGame,
|
||||
[LobbyData.UITargets.UI_FRLOBBYLANGAME.id] = CoD.LobbyMenus.FRButtonsLANGame,
|
||||
[LobbyData.UITargets.UI_ZMLOBBYLANGAME.id] = CoD.LobbyMenus.ZMButtonsLAN,
|
||||
[LobbyData.UITargets.UI_ZMLOBBYONLINE.id] = CoD.LobbyMenus.ZMButtonsOnline,
|
||||
[LobbyData.UITargets.UI_ZMLOBBYONLINEPUBLICGAME.id] = CoD.LobbyMenus.ZMButtonsPublicGame,
|
||||
[LobbyData.UITargets.UI_ZMLOBBYONLINECUSTOMGAME.id] = CoD.LobbyMenus.ZMButtonsCustomGame,
|
||||
[LobbyData.UITargets.UI_MPLOBBYONLINETHEATER.id] = CoD.LobbyMenus.ButtonsTheaterGame,
|
||||
[LobbyData.UITargets.UI_ZMLOBBYONLINETHEATER.id] = CoD.LobbyMenus.ButtonsTheaterGame
|
||||
}
|
||||
|
||||
CoD.LobbyMenus.AddButtonsForTarget = function ( controller, id )
|
||||
local buttonFunc = targetButtons[id]
|
||||
local model = nil
|
||||
if Engine.IsLobbyActive( Enum.LobbyType.LOBBY_TYPE_GAME ) then
|
||||
model = Engine.GetModel( DataSources.LobbyRoot.getModel( controller ), "gameClient.isHost" )
|
||||
else
|
||||
model = Engine.GetModel( DataSources.LobbyRoot.getModel( controller ), "privateClient.isHost" )
|
||||
end
|
||||
local isLeader = nil
|
||||
if model ~= nil then
|
||||
isLeader = Engine.GetModelValue( model )
|
||||
else
|
||||
isLeader = 1
|
||||
end
|
||||
local result = {}
|
||||
buttonFunc( controller, result, isLeader )
|
||||
return result
|
||||
end
|
||||
|
||||
|
2
deps/curl
vendored
2
deps/curl
vendored
@ -1 +1 @@
|
||||
Subproject commit a13ef31d0fbbf98120b711746bd8802acaba6b0a
|
||||
Subproject commit 1c5ed24ee0e929a6f410fcc3729becfd2ee71211
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 53fdf5f9c73cb4fde599dd07e54bac8264f7b236
|
||||
Subproject commit 0df542cb70f621bbeec207be1949832fb1442479
|
2
deps/rapidjson
vendored
2
deps/rapidjson
vendored
@ -1 +1 @@
|
||||
Subproject commit 083f359f5c36198accc2b9360ce1e32a333231d9
|
||||
Subproject commit 949c771b03de448bdedea80c44a4a5f65284bfeb
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
||||
Subproject commit eb0e038b297f2c9877ed8b3515c6718a4b65d485
|
||||
Subproject commit 66588683b36042154ad35140bf9fcbb60c5d573c
|
@ -2,20 +2,31 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "auth.hpp"
|
||||
#include "party.hpp"
|
||||
#include "command.hpp"
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "profile_infos.hpp"
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <game/utils.hpp>
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/smbios.hpp>
|
||||
#include <utils/byte_buffer.hpp>
|
||||
#include <utils/info_string.hpp>
|
||||
#include <utils/cryptography.hpp>
|
||||
|
||||
#include <game/fragment_handler.hpp>
|
||||
|
||||
namespace auth
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::array<uint64_t, 18> client_xuids{};
|
||||
|
||||
std::string get_hdd_serial()
|
||||
{
|
||||
DWORD serial{};
|
||||
@ -92,15 +103,175 @@ namespace auth
|
||||
return !is_first;
|
||||
}
|
||||
|
||||
std::string serialize_connect_data(const char* data, const int length)
|
||||
{
|
||||
utils::byte_buffer buffer{};
|
||||
profile_infos::get_profile_info().value_or(profile_infos::profile_info{}).serialize(buffer);
|
||||
|
||||
buffer.write_string(data, static_cast<size_t>(length));
|
||||
|
||||
return buffer.move_buffer();
|
||||
}
|
||||
|
||||
void send_fragmented_connect_packet(const game::netsrc_t sock, game::netadr_t* adr, const char* data,
|
||||
const int length)
|
||||
{
|
||||
const auto connect_data = serialize_connect_data(data, length);
|
||||
game::fragment_handler::fragment_data //
|
||||
(connect_data.data(), connect_data.size(), [&](const utils::byte_buffer& buffer)
|
||||
{
|
||||
utils::byte_buffer packet_buffer{};
|
||||
packet_buffer.write("connect");
|
||||
packet_buffer.write(" ");
|
||||
packet_buffer.write(buffer);
|
||||
|
||||
const auto& fragment_packet = packet_buffer.get_buffer();
|
||||
|
||||
game::NET_OutOfBandData(
|
||||
sock, adr, fragment_packet.data(),
|
||||
static_cast<int>(fragment_packet.size()));
|
||||
});
|
||||
}
|
||||
|
||||
int send_connect_data_stub(const game::netsrc_t sock, game::netadr_t* adr, const char* data, const int len)
|
||||
{
|
||||
/*const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
||||
if (is_connect_sequence)
|
||||
try
|
||||
{
|
||||
MessageBoxA(0, "CONNECT", 0, 0);
|
||||
}*/
|
||||
const auto is_connect_sequence = len >= 7 && strncmp("connect", data, 7) == 0;
|
||||
if (!is_connect_sequence)
|
||||
{
|
||||
return game::NET_OutOfBandData(sock, adr, data, len);
|
||||
}
|
||||
|
||||
return reinterpret_cast<decltype(&send_connect_data_stub)>(0x142173600_g)(sock, adr, data, len);
|
||||
send_fragmented_connect_packet(sock, adr, data, len);
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
printf("Error: %s\n", e.what());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void distribute_player_xuid(const game::netadr_t& target, const size_t player_index, const uint64_t xuid)
|
||||
{
|
||||
if (player_index >= 18)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(static_cast<uint32_t>(player_index));
|
||||
buffer.write(xuid);
|
||||
|
||||
game::foreach_connected_client([&](const game::client_s& client, const size_t index)
|
||||
{
|
||||
if (client.address.type != game::NA_BOT)
|
||||
{
|
||||
network::send(client.address, "playerXuid", buffer.get_buffer());
|
||||
}
|
||||
|
||||
if (index != player_index && target.type != game::NA_BOT)
|
||||
{
|
||||
utils::byte_buffer current_buffer{};
|
||||
current_buffer.write(static_cast<uint32_t>(index));
|
||||
current_buffer.write(client.xuid);
|
||||
|
||||
network::send(target, "playerXuid", current_buffer.get_buffer());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handle_new_player(const game::netadr_t& target)
|
||||
{
|
||||
const command::params_sv params{};
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const utils::info_string info_string(params[1]);
|
||||
const auto xuid = strtoull(info_string.get("xuid").data(), nullptr, 16);
|
||||
|
||||
size_t player_index = 18;
|
||||
game::foreach_connected_client([&](game::client_s& client, const size_t index)
|
||||
{
|
||||
if (client.address == target)
|
||||
{
|
||||
client.xuid = xuid;
|
||||
player_index = index;
|
||||
}
|
||||
});
|
||||
|
||||
distribute_player_xuid(target, player_index, xuid);
|
||||
}
|
||||
|
||||
void dispatch_connect_packet(const game::netadr_t& target, const std::string& data)
|
||||
{
|
||||
utils::byte_buffer buffer(data);
|
||||
const profile_infos::profile_info info(buffer);
|
||||
|
||||
const auto connect_data = buffer.read_string();
|
||||
const command::params_sv params(connect_data);
|
||||
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto _ = profile_infos::acquire_profile_lock();
|
||||
|
||||
const utils::info_string info_string(params[1]);
|
||||
const auto xuid = strtoull(info_string.get("xuid").data(), nullptr, 16);
|
||||
|
||||
profile_infos::add_and_distribute_profile_info(target, xuid, info);
|
||||
|
||||
game::SV_DirectConnect(target);
|
||||
handle_new_player(target);
|
||||
}
|
||||
|
||||
void handle_connect_packet_fragment(const game::netadr_t& target, const network::data_view& data)
|
||||
{
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer(data);
|
||||
|
||||
std::string final_packet{};
|
||||
if (game::fragment_handler::handle(target, buffer, final_packet))
|
||||
{
|
||||
scheduler::once([t = target, p = std::move(final_packet)]
|
||||
{
|
||||
dispatch_connect_packet(t, p);
|
||||
}, scheduler::server);
|
||||
}
|
||||
}
|
||||
|
||||
void handle_player_xuid_packet(const game::netadr_t& target, const network::data_view& data)
|
||||
{
|
||||
if (game::is_server_running() || !party::is_host(target))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer(data);
|
||||
|
||||
const auto player_id = buffer.read<uint32_t>();
|
||||
const auto xuid = buffer.read<uint64_t>();
|
||||
|
||||
if (player_id < client_xuids.size())
|
||||
{
|
||||
client_xuids[player_id] = xuid;
|
||||
}
|
||||
}
|
||||
|
||||
void direct_connect_bots_stub(const game::netadr_t address)
|
||||
{
|
||||
game::SV_DirectConnect(address);
|
||||
handle_new_player(address);
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,10 +290,52 @@ namespace auth
|
||||
return guid;
|
||||
}
|
||||
|
||||
uint64_t get_guid(const size_t client_num)
|
||||
{
|
||||
if (client_num >= 18)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
return client_xuids[client_num];
|
||||
}
|
||||
|
||||
uint64_t xuid = 0;
|
||||
const auto callback = [&xuid](const game::client_s& client)
|
||||
{
|
||||
xuid = client.xuid;
|
||||
};
|
||||
|
||||
if (!game::access_connected_client(client_num, callback))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return xuid;
|
||||
}
|
||||
|
||||
void clear_stored_guids()
|
||||
{
|
||||
for (auto& xuid : client_xuids)
|
||||
{
|
||||
xuid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Skip connect handler
|
||||
utils::hook::set<uint8_t>(game::select(0x142253EFA, 0x14053714A), 0xEB);
|
||||
network::on("connect", handle_connect_packet_fragment);
|
||||
network::on("playerXuid", handle_player_xuid_packet);
|
||||
|
||||
// Intercept SV_DirectConnect in SV_AddTestClient
|
||||
utils::hook::call(game::select(0x1422490DC, 0x14052E582), direct_connect_bots_stub);
|
||||
|
||||
// Patch steam id bit check
|
||||
std::vector<std::pair<size_t, size_t>> patches{};
|
||||
const auto p = [&patches](const size_t a, const size_t b)
|
||||
|
@ -3,4 +3,6 @@
|
||||
namespace auth
|
||||
{
|
||||
uint64_t get_guid();
|
||||
uint64_t get_guid(size_t client_num);
|
||||
void clear_stored_guids();
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ namespace bots
|
||||
entry = entry.substr(0, pos);
|
||||
}
|
||||
|
||||
bot_names.emplace_back(std::make_pair(entry, clan_abbrev));
|
||||
bot_names.emplace_back(entry, clan_abbrev);
|
||||
}
|
||||
|
||||
return bot_names;
|
||||
|
@ -122,7 +122,7 @@ namespace chat
|
||||
// Overwrite say command
|
||||
utils::hook::jump(0x14052A6C0_g, +[]
|
||||
{
|
||||
if (!game::get_dvar_bool("sv_running"))
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
printf("Server is not running\n");
|
||||
return;
|
||||
@ -138,7 +138,7 @@ namespace chat
|
||||
// Overwrite tell command
|
||||
utils::hook::jump(0x14052A7E0_g, +[]
|
||||
{
|
||||
if (!game::get_dvar_bool("sv_running"))
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
printf("Server is not running\n");
|
||||
return;
|
||||
|
@ -116,6 +116,9 @@ namespace client_patches
|
||||
{
|
||||
fix_amd_cpu_stuttering();
|
||||
|
||||
// Don't modify process priority
|
||||
utils::hook::nop(0x142334C98_g, 6);
|
||||
|
||||
// Kill microphones for now
|
||||
utils::hook::set(0x15AAE9254_g, mixer_open_stub);
|
||||
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "auth.hpp"
|
||||
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@ -10,8 +14,25 @@ namespace colors
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour get_player_name_hook;
|
||||
utils::hook::detour get_gamer_tag_hook;
|
||||
utils::hook::detour cl_get_client_name_hook;
|
||||
|
||||
std::optional<int> get_color_for_xuid(const uint64_t xuid)
|
||||
{
|
||||
if (xuid == 0xCD02AF6448291209
|
||||
|| xuid == 0x10F0C433E08E1357
|
||||
|| xuid == 0x60E0FEFE42341715)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<int> get_color_for_client(const int client_num)
|
||||
{
|
||||
const auto xuid = auth::get_guid(static_cast<size_t>(client_num));
|
||||
return get_color_for_xuid(xuid);
|
||||
}
|
||||
|
||||
template <size_t index>
|
||||
void patch_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a = 255)
|
||||
@ -36,23 +57,39 @@ namespace colors
|
||||
utils::hook::copy(g_color_table + index * 4, color_float, sizeof(color_float));
|
||||
}
|
||||
|
||||
/*uint64_t get_player_name_stub(const uint64_t client, int client_num, char* buffer, const int size,
|
||||
const bool has_clan_tag)
|
||||
bool cl_get_client_name_stub(const int local_client_num, const int index, char* buf, const int size,
|
||||
const bool add_clan_name)
|
||||
{
|
||||
const auto res = get_player_name_hook.invoke<uint64_t>(client, client_num, buffer, size, has_clan_tag);
|
||||
const auto res = cl_get_client_name_hook.invoke<bool>(local_client_num, index, buf, size, add_clan_name);
|
||||
|
||||
if (_ReturnAddress() != reinterpret_cast<void*>(0x1406A7B56_g))
|
||||
if (_ReturnAddress() == reinterpret_cast<void*>(0x1406A7B56_g))
|
||||
{
|
||||
const auto val = utils::string::va("^%d%s", rand() % 7, buffer);
|
||||
strncpy_s(buffer, size, val, size);
|
||||
return res;
|
||||
}
|
||||
|
||||
return res;
|
||||
}*/
|
||||
|
||||
/*const char* get_gamer_tag_stub(const uint64_t num)
|
||||
const auto color = get_color_for_client(index);
|
||||
if (!color)
|
||||
{
|
||||
return utils::string::va("^3%s", get_gamer_tag_hook.invoke<const char*>(num));
|
||||
return res;
|
||||
}
|
||||
|
||||
const auto val = utils::string::va("^%d%s", *color, buf);
|
||||
utils::string::copy(buf, size, val);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*const char* get_gamer_tag_stub(const uint32_t num)
|
||||
{
|
||||
const auto color = get_color_for_xuid(steam::SteamUser()->GetSteamID().bits);
|
||||
const auto name = reinterpret_cast<const char* (*)(uint32_t)>(0x141EC6E80)(num) + 8;
|
||||
|
||||
if (!color || num)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
return utils::string::va("^1%s", *color, name);
|
||||
}*/
|
||||
}
|
||||
|
||||
@ -68,8 +105,8 @@ namespace colors
|
||||
patch_color<6>(151, 80, 221); // 6 - Pink
|
||||
|
||||
// Old addresses
|
||||
//get_player_name_hook.create(0x1413E3140_g, get_player_name_stub);
|
||||
//get_gamer_tag_hook.create(0x141EC7370_g, get_gamer_tag_stub);
|
||||
cl_get_client_name_hook.create(game::CL_GetClientName, cl_get_client_name_stub);
|
||||
//utils::hook::jump(0x141EC72E0_g, get_gamer_tag_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <utils/memory.hpp>
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <steam/steam.hpp>
|
||||
|
||||
namespace command
|
||||
{
|
||||
@ -66,15 +67,6 @@ namespace command
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Disable whitelist
|
||||
utils::hook::jump(game::select(0x1420EE860, 0x1404F9CD0), update_whitelist_stub);
|
||||
}
|
||||
};
|
||||
|
||||
params::params()
|
||||
: nesting_(get_cmd_args()->nesting)
|
||||
{
|
||||
@ -227,6 +219,15 @@ namespace command
|
||||
game::Cmd_AddServerCommandInternal(cmd_string, execute_custom_sv_command,
|
||||
allocator.allocate<game::cmd_function_s>());
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
// Disable whitelist
|
||||
utils::hook::jump(game::select(0x1420EE860, 0x1404F9CD0), update_whitelist_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(command::component)
|
||||
|
@ -15,6 +15,8 @@ namespace dedicated
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const game::dvar_t* sv_lan_only;
|
||||
|
||||
void sv_con_tell_f_stub(game::client_s* cl_0, game::svscmd_type type, [[maybe_unused]] const char* fmt,
|
||||
[[maybe_unused]] int c, char* text)
|
||||
{
|
||||
@ -23,6 +25,11 @@ namespace dedicated
|
||||
|
||||
void send_heartbeat_packet()
|
||||
{
|
||||
if (sv_lan_only->current.value.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
game::netadr_t target{};
|
||||
if (server_list::get_master_server(target))
|
||||
{
|
||||
@ -64,11 +71,14 @@ namespace dedicated
|
||||
// Fix tell command for IW4M
|
||||
utils::hook::call(0x14052A8CF_g, sv_con_tell_f_stub);
|
||||
|
||||
scheduler::once(send_heartbeat, scheduler::pipeline::main);
|
||||
scheduler::loop(send_heartbeat, scheduler::pipeline::main, 5min);
|
||||
command::add("heartbeat", send_heartbeat);
|
||||
|
||||
// Hook GScr_ExitLevel
|
||||
utils::hook::jump(0x1402D1AA0_g, trigger_map_rotation);
|
||||
|
||||
sv_lan_only = game::register_dvar_bool("sv_lanOnly", false, game::DVAR_NONE, "Don't send heartbeats");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -27,11 +27,11 @@ namespace dedicated_info
|
||||
|
||||
const auto mapname = game::get_dvar_string("mapname");
|
||||
|
||||
const std::string window_text = utils::string::va("%s on %s [%d/%d] (%d)",
|
||||
const std::string window_text = utils::string::va("%s on %s [%zu/%zu] (%zu)",
|
||||
clean_server_name,
|
||||
mapname.data(),
|
||||
getinfo::get_client_count(),
|
||||
getinfo::get_max_client_count(),
|
||||
game::get_max_client_count(),
|
||||
getinfo::get_bot_count());
|
||||
|
||||
console::set_title(window_text);
|
||||
|
@ -36,22 +36,22 @@ namespace dedicated_patches
|
||||
{
|
||||
const std::vector<uintptr_t> is_mod_loaded_addresses =
|
||||
{
|
||||
{ 0x14019CFC4_g },
|
||||
{ 0x14024D4A0_g },
|
||||
{ 0x14024D669_g },
|
||||
{ 0x14024D939_g },
|
||||
{ 0x14024DC64_g },
|
||||
{ 0x14024E13A_g },
|
||||
{ 0x14024E5A3_g },
|
||||
{ 0x14024FFB9_g },
|
||||
{ 0x140251E9E_g },
|
||||
{ 0x140253680_g },
|
||||
{ 0x140257BF6_g },
|
||||
{ 0x1402D296D_g },
|
||||
{ 0x1402D58E9_g },
|
||||
{ 0x140468374_g },
|
||||
{ 0x14046B796_g },
|
||||
{ 0x14048003D_g },
|
||||
{0x14019CFC4_g},
|
||||
{0x14024D4A0_g},
|
||||
{0x14024D669_g},
|
||||
{0x14024D939_g},
|
||||
{0x14024DC64_g},
|
||||
{0x14024E13A_g},
|
||||
{0x14024E5A3_g},
|
||||
{0x14024FFB9_g},
|
||||
{0x140251E9E_g},
|
||||
{0x140253680_g},
|
||||
{0x140257BF6_g},
|
||||
{0x1402D296D_g},
|
||||
{0x1402D58E9_g},
|
||||
{0x140468374_g},
|
||||
{0x14046B796_g},
|
||||
{0x14048003D_g},
|
||||
};
|
||||
|
||||
for (const auto& address : is_mod_loaded_addresses)
|
||||
@ -68,25 +68,20 @@ namespace dedicated_patches
|
||||
spawn_server_hook.invoke(controllerIndex, server, preload, savegame);
|
||||
}
|
||||
|
||||
uint64_t sv_get_player_xuid_stub(int client_num)
|
||||
uint64_t sv_get_player_xuid_stub(const int client_num)
|
||||
{
|
||||
return static_cast<uint64_t>(game::svs_clients[client_num].xuid);
|
||||
}
|
||||
|
||||
int sv_get_guid(int client_num)
|
||||
{
|
||||
if (client_num < 0 || client_num >= game::Dvar_GetInt(*game::com_maxclients))
|
||||
const auto* clients = *game::svs_clients;
|
||||
if (!clients)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return game::svs_clients[client_num].xuid;
|
||||
return clients[client_num].xuid;
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : server_component
|
||||
{
|
||||
static_assert(offsetof(game::client_s, xuid) == 0xBB354);
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
@ -109,7 +104,9 @@ namespace dedicated_patches
|
||||
utils::hook::jump(0x14052F0F5_g, 0x14052F139_g);
|
||||
|
||||
utils::hook::call(0x1402853D7_g, sv_get_player_xuid_stub); // PlayerCmd_GetXuid
|
||||
utils::hook::call(0x140283303_g, sv_get_guid); // PlayerCmd_GetGuid
|
||||
|
||||
// Stop executing default_dedicated.cfg & language_settings.cfg
|
||||
utils::hook::set<uint8_t>(0x1405063C0_g, 0xC3);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
133
src/client/component/game_settings.cpp
Normal file
133
src/client/component/game_settings.cpp
Normal file
@ -0,0 +1,133 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace gamesettings
|
||||
{
|
||||
namespace
|
||||
{
|
||||
// <name, path>
|
||||
std::unordered_map<std::string, std::string> game_settings_files;
|
||||
|
||||
std::string get_game_settings_name(const std::vector<std::string>& sub_strings)
|
||||
{
|
||||
if (sub_strings.size() > 2)
|
||||
{
|
||||
return sub_strings[sub_strings.size() - 2] + '/' + sub_strings[sub_strings.size() - 1];
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string get_game_settings_path(const std::string& name)
|
||||
{
|
||||
const auto itr = game_settings_files.find(name);
|
||||
return (itr == game_settings_files.end()) ? std::string() : itr->second;
|
||||
}
|
||||
|
||||
void search_game_settings_folder(const std::string& game_settings_dir)
|
||||
{
|
||||
if (!utils::io::directory_exists(game_settings_dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto files = utils::io::list_files(game_settings_dir, true);
|
||||
|
||||
for (const auto& path : files)
|
||||
{
|
||||
if (!std::filesystem::is_directory(path))
|
||||
{
|
||||
auto sub_strings = utils::string::split(path.generic_string(), '/');
|
||||
game_settings_files.insert_or_assign(get_game_settings_name(sub_strings), path.generic_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_game_settings_file_on_disk(const char* path)
|
||||
{
|
||||
if (!path)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto sub_strings = utils::string::split(path, '/');
|
||||
const auto game_settings_name = get_game_settings_name(sub_strings);
|
||||
|
||||
return !get_game_settings_path(game_settings_name).empty();
|
||||
}
|
||||
|
||||
void cmd_exec_stub(utils::hook::assembler& a)
|
||||
{
|
||||
const auto exec_from_fastfile = a.newLabel();
|
||||
const auto exec_from_disk = a.newLabel();
|
||||
|
||||
a.pushad64();
|
||||
|
||||
a.mov(rcx, r10);
|
||||
a.call_aligned(has_game_settings_file_on_disk);
|
||||
a.cmp(rax, 1);
|
||||
;
|
||||
a.popad64();
|
||||
|
||||
a.jnz(exec_from_fastfile);
|
||||
|
||||
a.bind(exec_from_disk);
|
||||
a.jmp(game::select(0x1420ED087, 0x1404F855E));
|
||||
|
||||
a.bind(exec_from_fastfile);
|
||||
a.lea(rdx, ptr(rsp, (game::is_server() ? 0x30 : 0x40)));
|
||||
a.jmp(game::select(0x1420ED007, 0x1404F853F));
|
||||
}
|
||||
|
||||
int read_file_stub(const char* qpath, void** buffer)
|
||||
{
|
||||
const auto sub_strings = utils::string::split(qpath, '/');
|
||||
const auto game_settings_name = get_game_settings_name(sub_strings);
|
||||
|
||||
std::string gamesettings_data;
|
||||
utils::io::read_file(get_game_settings_path(game_settings_name), &gamesettings_data);
|
||||
|
||||
if (!gamesettings_data.empty())
|
||||
{
|
||||
++(*game::fs_loadStack);
|
||||
|
||||
auto len = static_cast<int>(gamesettings_data.length());
|
||||
auto buf = game::FS_AllocMem(len + 1);
|
||||
|
||||
*buffer = buf;
|
||||
gamesettings_data.copy(reinterpret_cast<char*>(*buffer), len);
|
||||
buf[len] = '\0';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
return utils::hook::invoke<int>(game::select(0x1422A48D0, 0x140564F70), qpath, buffer);
|
||||
}
|
||||
|
||||
void search_gamesettings_files_on_disk()
|
||||
{
|
||||
const utils::nt::library host{};
|
||||
|
||||
search_game_settings_folder((game::get_appdata_path() / "data/gamesettings").string());
|
||||
search_game_settings_folder((host.get_folder() / "boiii/gamesettings").string());
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
search_gamesettings_files_on_disk();
|
||||
|
||||
utils::hook::call(game::select(0x1420ED0A1, 0x1404F857D), read_file_stub);
|
||||
utils::hook::jump(game::select(0x1420ED002, 0x1404F853A), utils::hook::assemble(cmd_exec_stub));
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
REGISTER_COMPONENT(gamesettings::component)
|
@ -5,6 +5,7 @@
|
||||
#include "steam/steam.hpp"
|
||||
|
||||
#include "network.hpp"
|
||||
#include "workshop.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -21,21 +22,18 @@ namespace getinfo
|
||||
return game::get_dvar_int("com_maxclients");
|
||||
}
|
||||
|
||||
int get_client_count()
|
||||
template <typename T>
|
||||
int get_client_count(T* client_states)
|
||||
{
|
||||
int count = 0;
|
||||
const auto client_states = *reinterpret_cast<uint64_t*>(game::select(0x1576F9318, 0x14A178E98));
|
||||
if (!client_states)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto object_length = game::is_server() ? 0xE5110 : 0xE5170;
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < get_max_client_count(); ++i)
|
||||
{
|
||||
const auto client_state = *reinterpret_cast<int*>(client_states + (i * object_length));
|
||||
if (client_state > 0)
|
||||
if (client_states[i].client_state > 0)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
@ -44,23 +42,28 @@ namespace getinfo
|
||||
return count;
|
||||
}
|
||||
|
||||
int get_bot_count()
|
||||
size_t get_client_count()
|
||||
{
|
||||
const auto client_states = *reinterpret_cast<uint64_t*>(game::select(0x1576F9318, 0x14A178E98));
|
||||
if (!client_states)
|
||||
size_t count = 0;
|
||||
game::foreach_connected_client([&count](const game::client_s&)
|
||||
{
|
||||
return 0;
|
||||
++count;
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < get_max_client_count(); ++i)
|
||||
size_t get_bot_count()
|
||||
{
|
||||
if (game::SV_IsTestClient(i))
|
||||
size_t count = 0;
|
||||
|
||||
game::foreach_connected_client([&count](const game::client_s&, const size_t index)
|
||||
{
|
||||
if (game::SV_IsTestClient(static_cast<int>(index)))
|
||||
{
|
||||
++count;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return count;
|
||||
}
|
||||
@ -92,7 +95,7 @@ namespace getinfo
|
||||
network::on("getInfo", [](const game::netadr_t& target, const network::data_view& data)
|
||||
{
|
||||
utils::info_string info{};
|
||||
info.set("challenge", std::string(data.begin(), data.end()));
|
||||
info.set("challenge", std::string{ data.begin(), data.end() });
|
||||
info.set("gamename", "T7");
|
||||
info.set("hostname",
|
||||
game::get_dvar_string(game::is_server() ? "live_steam_server_name" : "sv_hostname"));
|
||||
@ -103,14 +106,17 @@ namespace getinfo
|
||||
info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits));
|
||||
info.set("mapname", game::get_dvar_string("mapname"));
|
||||
info.set("isPrivate", game::get_dvar_string("g_password").empty() ? "0" : "1");
|
||||
info.set("clients", utils::string::va("%i", get_client_count()));
|
||||
info.set("bots", utils::string::va("%i", get_bot_count()));
|
||||
info.set("sv_maxclients", utils::string::va("%i", get_max_client_count()));
|
||||
info.set("protocol", utils::string::va("%i", PROTOCOL));
|
||||
info.set("playmode", utils::string::va("%i", game::Com_SessionMode_GetMode()));
|
||||
info.set("gamemode", utils::string::va("%i", Com_SessionMode_GetGameMode()));
|
||||
//info.set("sv_running", utils::string::va("%i", get_dvar_bool("sv_running")));
|
||||
info.set("dedicated", utils::string::va("%i", game::is_server() ? 1 : 0));
|
||||
info.set("clients", std::to_string(get_client_count()));
|
||||
info.set("bots", std::to_string(get_bot_count()));
|
||||
info.set("sv_maxclients", std::to_string(get_max_client_count()));
|
||||
info.set("protocol", std::to_string(PROTOCOL));
|
||||
info.set("playmode", std::to_string(game::Com_SessionMode_GetMode()));
|
||||
info.set("gamemode", std::to_string(Com_SessionMode_GetGameMode()));
|
||||
info.set("sv_running", std::to_string(game::is_server_running()));
|
||||
info.set("dedicated", game::is_server() ? "1" : "0");
|
||||
info.set("hc", std::to_string(game::Com_GametypeSettings_GetUInt("hardcoremode", false)));
|
||||
info.set("modname", workshop::get_mod_name(game::get_dvar_string("fs_game")));
|
||||
info.set("fs_game", game::get_dvar_string("fs_game"));
|
||||
info.set("shortversion", SHORTVERSION);
|
||||
|
||||
network::send(target, "infoResponse", info.build(), '\n');
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
namespace getinfo
|
||||
{
|
||||
int get_max_client_count();
|
||||
int get_client_count();
|
||||
int get_bot_count();
|
||||
size_t get_client_count();
|
||||
size_t get_bot_count();
|
||||
bool is_host();
|
||||
}
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "network.hpp"
|
||||
|
||||
#include "game/fragment_handler.hpp"
|
||||
|
||||
namespace network
|
||||
{
|
||||
namespace
|
||||
@ -35,7 +37,18 @@ namespace network
|
||||
|
||||
const std::basic_string_view data(message->data + offset, message->cursize - offset);
|
||||
|
||||
try
|
||||
{
|
||||
handler->second(*address, data);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
printf("Error: %s\n", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -150,7 +163,6 @@ namespace network
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
uint64_t ret2()
|
||||
{
|
||||
return 2;
|
||||
@ -160,6 +172,15 @@ namespace network
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void com_error_oob_stub(const char* file, int line, int code, [[maybe_unused]] const char* fmt, const char* error)
|
||||
{
|
||||
char buffer[1024]{};
|
||||
|
||||
strncpy_s(buffer, error, _TRUNCATE);
|
||||
|
||||
game::Com_Error_(file, line, code, "%s", buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void on(const std::string& command, const callback& callback)
|
||||
@ -245,10 +266,44 @@ namespace network
|
||||
return a.port == b.port && a.addr == b.addr;
|
||||
}
|
||||
|
||||
int net_sendpacket_stub(const game::netsrc_t sock, const int length, const char* data, const game::netadr_t* to)
|
||||
{
|
||||
//printf("Sending packet of size: %X\n", length);
|
||||
|
||||
if (to->type != game::NA_RAWIP)
|
||||
{
|
||||
printf("NET_SendPacket: bad address type\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto s = *game::ip_socket;
|
||||
if (!s || sock > game::NS_MAXCLIENTS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in address{};
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = htons(to->port);
|
||||
address.sin_addr.s_addr = htonl(((to->ipv4.c | ((to->ipv4.b | (to->ipv4.a << 8)) << 8)) << 8) | to->ipv4.d);
|
||||
|
||||
const auto size = static_cast<size_t>(length);
|
||||
|
||||
std::vector<char> buffer{};
|
||||
buffer.resize(size + 1);
|
||||
buffer[0] = static_cast<char>((static_cast<uint32_t>(sock) & 0xF) | ((to->localNetID & 0xF) << 4));
|
||||
memcpy(buffer.data() + 1, data, size);
|
||||
|
||||
return sendto(s, buffer.data(), static_cast<int>(buffer.size()), 0, reinterpret_cast<sockaddr*>(&address),
|
||||
sizeof(address));
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_unpack() override
|
||||
{
|
||||
scheduler::loop(game::fragment_handler::clean, scheduler::async, 5s);
|
||||
|
||||
utils::hook::nop(game::select(0x1423322B6, 0x140596DF6), 4);
|
||||
// don't increment data pointer to optionally skip socket byte
|
||||
utils::hook::call(game::select(0x142332283, 0x140596DC3), read_socket_byte_stub);
|
||||
@ -257,6 +312,9 @@ namespace network
|
||||
// skip checksum verification
|
||||
utils::hook::set<uint8_t>(game::select(0x14233249E, 0x140596F2E), 0); // don't add checksum to packet
|
||||
|
||||
// Recreate NET_SendPacket to increase max packet size
|
||||
//utils::hook::jump(game::select(0x1423323B0, 0x140596E40), net_sendpacket_stub);
|
||||
|
||||
utils::hook::set<uint32_t>(game::select(0x14134C6E0, 0x14018E574), 5);
|
||||
// set initial connection state to challenging
|
||||
|
||||
@ -271,10 +329,21 @@ namespace network
|
||||
// NA_IP -> NA_RAWIP in NetAdr_ToString
|
||||
utils::hook::set<uint8_t>(game::select(0x142172ED4, 0x140515864), game::NA_RAWIP);
|
||||
|
||||
// Kill 'echo' OOB handler
|
||||
utils::hook::set<uint8_t>(game::select(0x14134D0FB, 0x14018EE82), 0xEB);
|
||||
|
||||
if (game::is_server())
|
||||
{
|
||||
// Remove restrictions for rcon commands
|
||||
utils::hook::call(0x140538D5C_g, &con_restricted_execute_buf_stub); // SVC_RemoteCommand
|
||||
utils::hook::call(0x140538D5C_g, con_restricted_execute_buf_stub); // SVC_RemoteCommand
|
||||
|
||||
// Kill 'error' OOB handler on the dedi
|
||||
utils::hook::nop(0x14018EF8B_g, 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Truncate error string to make sure there are no buffer overruns later
|
||||
utils::hook::call(0x14134D206_g, com_error_oob_stub);
|
||||
}
|
||||
|
||||
// TODO: Fix that
|
||||
|
@ -3,9 +3,11 @@
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "party.hpp"
|
||||
#include "auth.hpp"
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "workshop.hpp"
|
||||
#include "profile_infos.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -36,12 +38,14 @@ namespace party
|
||||
}
|
||||
|
||||
void connect_to_lobby(const game::netadr_t& addr, const std::string& mapname, const std::string& gamemode,
|
||||
const std::string& pub_id)
|
||||
const std::string& usermap_id, const std::string& mod_id)
|
||||
{
|
||||
workshop::load_usermap_mod_if_needed(pub_id);
|
||||
auth::clear_stored_guids();
|
||||
|
||||
workshop::load_mod_if_needed(usermap_id, mod_id);
|
||||
|
||||
game::XSESSION_INFO info{};
|
||||
game::CL_ConnectFromLobby(0, &info, &addr, 1, 0, mapname.data(), gamemode.data(), pub_id.data());
|
||||
game::CL_ConnectFromLobby(0, &info, &addr, 1, 0, mapname.data(), gamemode.data(), usermap_id.data());
|
||||
}
|
||||
|
||||
void launch_mode(const game::eModes mode)
|
||||
@ -55,11 +59,13 @@ namespace party
|
||||
}
|
||||
|
||||
void connect_to_lobby_with_mode(const game::netadr_t& addr, const game::eModes mode, const std::string& mapname,
|
||||
const std::string& gametype, const std::string& pub_id, const bool was_retried = false)
|
||||
const std::string& gametype, const std::string& usermap_id,
|
||||
const std::string& mod_id,
|
||||
const bool was_retried = false)
|
||||
{
|
||||
if (game::Com_SessionMode_IsMode(mode))
|
||||
{
|
||||
connect_to_lobby(addr, mapname, gametype, pub_id);
|
||||
connect_to_lobby(addr, mapname, gametype, usermap_id, mod_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -67,7 +73,7 @@ namespace party
|
||||
{
|
||||
scheduler::once([=]
|
||||
{
|
||||
connect_to_lobby_with_mode(addr, mode, mapname, gametype, pub_id, true);
|
||||
connect_to_lobby_with_mode(addr, mode, mapname, gametype, usermap_id, mod_id, true);
|
||||
}, scheduler::main, 5s);
|
||||
|
||||
launch_mode(mode);
|
||||
@ -144,6 +150,13 @@ namespace party
|
||||
|
||||
is_connecting_to_dedi = info.get("dedicated") == "1";
|
||||
|
||||
if (atoi(info.get("protocol").data()) != PROTOCOL)
|
||||
{
|
||||
const auto str = "Invalid protocol.";
|
||||
printf("%s\n", str);
|
||||
return;
|
||||
}
|
||||
|
||||
const auto gamename = info.get("gamename");
|
||||
if (gamename != "T7"s)
|
||||
{
|
||||
@ -168,6 +181,8 @@ namespace party
|
||||
return;
|
||||
}
|
||||
|
||||
const auto mod_id = info.get("fs_game");
|
||||
|
||||
//const auto hostname = info.get("sv_hostname");
|
||||
const auto playmode = info.get("playmode");
|
||||
const auto mode = static_cast<game::eModes>(std::atoi(playmode.data()));
|
||||
@ -175,9 +190,10 @@ namespace party
|
||||
|
||||
scheduler::once([=]
|
||||
{
|
||||
const auto publisher_id = workshop::get_usermap_publisher_id(mapname);
|
||||
const auto usermap_id = workshop::get_usermap_publisher_id(mapname);
|
||||
|
||||
if (workshop::check_valid_publisher_id(mapname, publisher_id))
|
||||
if (workshop::check_valid_usermap_id(mapname, usermap_id) &&
|
||||
workshop::check_valid_mod_id(mod_id))
|
||||
{
|
||||
if (is_connecting_to_dedi)
|
||||
{
|
||||
@ -185,7 +201,7 @@ namespace party
|
||||
}
|
||||
|
||||
//connect_to_session(target, hostname, xuid, mode);
|
||||
connect_to_lobby_with_mode(target, mode, mapname, gametype, publisher_id);
|
||||
connect_to_lobby_with_mode(target, mode, mapname, gametype, usermap_id, mod_id);
|
||||
}
|
||||
}, scheduler::main);
|
||||
}
|
||||
@ -203,6 +219,7 @@ namespace party
|
||||
connect_host = target;
|
||||
}
|
||||
|
||||
profile_infos::clear_profile_infos();
|
||||
query_server(connect_host, handle_connect_query_response);
|
||||
}
|
||||
|
||||
@ -306,6 +323,11 @@ namespace party
|
||||
return *reinterpret_cast<game::netadr_t*>(address);
|
||||
}
|
||||
|
||||
bool is_host(const game::netadr_t& addr)
|
||||
{
|
||||
return get_connected_server() == addr || connect_host == addr;
|
||||
}
|
||||
|
||||
struct component final : client_component
|
||||
{
|
||||
void post_unpack() override
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include <utils/info_string.hpp>
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace party
|
||||
{
|
||||
using query_callback_func = void(bool success, const game::netadr_t& host, const ::utils::info_string& info, uint32_t ping);
|
||||
@ -9,4 +11,6 @@ namespace party
|
||||
void query_server(const game::netadr_t& host, query_callback callback);
|
||||
|
||||
game::netadr_t get_connected_server();
|
||||
|
||||
bool is_host(const game::netadr_t& addr);
|
||||
}
|
||||
|
@ -2,12 +2,31 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <game/utils.hpp>
|
||||
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace patches
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour sv_execute_client_messages_hook;
|
||||
|
||||
void sv_execute_client_messages_stub(game::client_s* client, game::msg_t* msg)
|
||||
{
|
||||
if (client->reliableAcknowledge < 0)
|
||||
{
|
||||
client->reliableAcknowledge = client->reliableSequence;
|
||||
network::send(client->address, "error", "EXE_LOSTRELIABLECOMMANDS");
|
||||
return;
|
||||
}
|
||||
|
||||
sv_execute_client_messages_hook.invoke<void>(client, msg);
|
||||
}
|
||||
|
||||
void script_errors_stub(const char* file, int line, unsigned int code, const char* fmt, ...)
|
||||
{
|
||||
char buffer[0x1000];
|
||||
@ -34,6 +53,14 @@ namespace patches
|
||||
utils::hook::set<uint8_t>(game::select(0x14224DA53, 0x140531143), 3);
|
||||
utils::hook::set<uint8_t>(game::select(0x14224DBB4, 0x1405312A8), 3);
|
||||
utils::hook::set<uint8_t>(game::select(0x14224DF8C, 0x1405316DC), 3);
|
||||
|
||||
// make sure client's reliableAck are not negative
|
||||
sv_execute_client_messages_hook.create(game::select(0x14224A460, 0x14052F840), sv_execute_client_messages_stub);
|
||||
|
||||
scheduler::once([]
|
||||
{
|
||||
game::register_dvar_string("password", "", game::DVAR_USERINFO, "password");
|
||||
}, scheduler::pipeline::main);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -3,20 +3,24 @@
|
||||
|
||||
#include "profile_infos.hpp"
|
||||
#include "network.hpp"
|
||||
#include "party.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/properties.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
#include "../steam/steam.hpp"
|
||||
#include <utils/io.hpp>
|
||||
|
||||
#include "game/utils.hpp"
|
||||
#include "game/fragment_handler.hpp"
|
||||
|
||||
namespace profile_infos
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using profile_map = std::unordered_map<uint64_t, profile_info>;
|
||||
utils::concurrency::container<profile_map> profile_mapping;
|
||||
utils::concurrency::container<profile_map, std::recursive_mutex> profile_mapping{};
|
||||
|
||||
std::optional<profile_info> load_profile_info()
|
||||
{
|
||||
@ -29,7 +33,7 @@ namespace profile_infos
|
||||
profile_info info{};
|
||||
constexpr auto version_size = sizeof(info.version);
|
||||
|
||||
if(data.size() < sizeof(version_size))
|
||||
if (data.size() < sizeof(version_size))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@ -37,17 +41,170 @@ namespace profile_infos
|
||||
memcpy(&info.version, data.data(), version_size);
|
||||
info.ddl.assign(data.begin() + version_size, data.end());
|
||||
|
||||
return { std::move(info) };
|
||||
}
|
||||
return {std::move(info)};
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info(uint64_t user_id)
|
||||
void send_profile_info(const game::netadr_t& address, const std::string& data)
|
||||
{
|
||||
game::fragment_handler::fragment_data(data.data(), data.size(), [&address](const utils::byte_buffer& buffer)
|
||||
{
|
||||
network::send(address, "profileInfo", buffer.get_buffer());
|
||||
});
|
||||
}
|
||||
|
||||
void distribute_profile_info(const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(user_id);
|
||||
info.serialize(buffer);
|
||||
|
||||
const std::string data = buffer.move_buffer();
|
||||
|
||||
game::foreach_connected_client([&](const game::client_s& client)
|
||||
{
|
||||
send_profile_info(client.address, data);
|
||||
});
|
||||
}
|
||||
|
||||
std::unordered_set<uint64_t> get_connected_client_xuids()
|
||||
{
|
||||
std::unordered_set<uint64_t> connected_clients{};
|
||||
connected_clients.reserve(game::get_max_client_count());
|
||||
|
||||
game::foreach_connected_client([&](const game::client_s& client)
|
||||
{
|
||||
connected_clients.emplace(client.xuid);
|
||||
});
|
||||
|
||||
return connected_clients;
|
||||
}
|
||||
|
||||
void clean_cached_profile_infos()
|
||||
{
|
||||
if (!game::is_server_running())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
profile_mapping.access([](profile_map& profiles)
|
||||
{
|
||||
const auto xuids = get_connected_client_xuids();
|
||||
|
||||
for (auto i = profiles.begin(); i != profiles.end();)
|
||||
{
|
||||
if (xuids.contains(i->first))
|
||||
{
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEV_BUILD
|
||||
printf("Erasing profile info: %llX\n", i->first);
|
||||
#endif
|
||||
|
||||
i = profiles.erase(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
profile_info::profile_info(utils::byte_buffer& buffer)
|
||||
{
|
||||
this->version = buffer.read<int32_t>();
|
||||
this->ddl = buffer.read_string();
|
||||
}
|
||||
|
||||
void profile_info::serialize(utils::byte_buffer& buffer) const
|
||||
{
|
||||
buffer.write(this->version);
|
||||
buffer.write_string(this->ddl);
|
||||
}
|
||||
|
||||
void add_profile_info(const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
printf("Adding profile info: %llX\n", user_id);
|
||||
#endif
|
||||
|
||||
profile_mapping.access([&](profile_map& profiles)
|
||||
{
|
||||
profiles[user_id] = info;
|
||||
});
|
||||
}
|
||||
|
||||
void distribute_profile_info_to_user(const game::netadr_t& addr, const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(user_id);
|
||||
info.serialize(buffer);
|
||||
|
||||
send_profile_info(addr, buffer.get_buffer());
|
||||
}
|
||||
|
||||
void distribute_profile_infos_to_user(const game::netadr_t& addr)
|
||||
{
|
||||
profile_mapping.access([&](const profile_map& profiles)
|
||||
{
|
||||
for (const auto& entry : profiles)
|
||||
{
|
||||
distribute_profile_info_to_user(addr, entry.first, entry.second);
|
||||
}
|
||||
});
|
||||
|
||||
if (!game::is_server())
|
||||
{
|
||||
const auto info = get_profile_info();
|
||||
if (info)
|
||||
{
|
||||
distribute_profile_info_to_user(addr, steam::SteamUser()->GetSteamID().bits, *info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_and_distribute_profile_info(const game::netadr_t& addr, const uint64_t user_id, const profile_info& info)
|
||||
{
|
||||
distribute_profile_infos_to_user(addr);
|
||||
|
||||
add_profile_info(user_id, info);
|
||||
distribute_profile_info(user_id, info);
|
||||
}
|
||||
|
||||
void clear_profile_infos()
|
||||
{
|
||||
profile_mapping.access([&](profile_map& profiles)
|
||||
{
|
||||
profiles = {};
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_lock<std::recursive_mutex> acquire_profile_lock()
|
||||
{
|
||||
return profile_mapping.acquire_lock();
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info()
|
||||
{
|
||||
return load_profile_info();
|
||||
}
|
||||
|
||||
std::optional<profile_info> get_profile_info(const uint64_t user_id)
|
||||
{
|
||||
if (user_id == steam::SteamUser()->GetSteamID().bits)
|
||||
{
|
||||
return get_profile_info();
|
||||
}
|
||||
|
||||
return profile_mapping.access<std::optional<profile_info>>([user_id](const profile_map& profiles)
|
||||
{
|
||||
std::optional<profile_info> result{};
|
||||
@ -56,7 +213,17 @@ namespace profile_infos
|
||||
if (profile_entry != profiles.end())
|
||||
{
|
||||
result = profile_entry->second;
|
||||
|
||||
#ifdef DEV_BUILD
|
||||
printf("Requesting profile info: %llX - good\n", user_id);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEV_BUILD
|
||||
else
|
||||
{
|
||||
printf("Requesting profile info: %llX - bad\n", user_id);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
});
|
||||
@ -75,15 +242,32 @@ namespace profile_infos
|
||||
|
||||
struct component final : generic_component
|
||||
{
|
||||
void post_load() override
|
||||
{
|
||||
}
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
/*network::on("profileInfo", [](const game::netadr_t& server, const network::data_view& data)
|
||||
scheduler::loop(clean_cached_profile_infos, scheduler::main, 5s);
|
||||
|
||||
if (game::is_client())
|
||||
{
|
||||
});*/
|
||||
network::on("profileInfo", [](const game::netadr_t& server, const network::data_view& data)
|
||||
{
|
||||
if (!party::is_host(server))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::byte_buffer buffer(data);
|
||||
|
||||
std::string final_packet{};
|
||||
if (game::fragment_handler::handle(server, buffer, final_packet))
|
||||
{
|
||||
buffer = utils::byte_buffer(final_packet);
|
||||
const auto user_id = buffer.read<uint64_t>();
|
||||
const profile_info info(buffer);
|
||||
|
||||
add_profile_info(user_id, info);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,13 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <game/game.hpp>
|
||||
#include <utils/byte_buffer.hpp>
|
||||
|
||||
namespace profile_infos
|
||||
{
|
||||
struct profile_info
|
||||
{
|
||||
int32_t version;
|
||||
std::string ddl;
|
||||
int32_t version{3};
|
||||
std::string ddl{};
|
||||
|
||||
profile_info() = default;
|
||||
profile_info(utils::byte_buffer& buffer);
|
||||
void serialize(utils::byte_buffer& buffer) const;
|
||||
};
|
||||
|
||||
void add_profile_info(uint64_t user_id, const profile_info& info);
|
||||
void add_and_distribute_profile_info(const game::netadr_t& addr, uint64_t user_id, const profile_info& info);
|
||||
void clear_profile_infos();
|
||||
|
||||
std::unique_lock<std::recursive_mutex> acquire_profile_lock();
|
||||
|
||||
std::optional<profile_info> get_profile_info();
|
||||
std::optional<profile_info> get_profile_info(uint64_t user_id);
|
||||
void update_profile_info(const profile_info& info);
|
||||
}
|
||||
|
@ -88,7 +88,6 @@ namespace scheduler
|
||||
task_pipeline pipelines[pipeline::count];
|
||||
|
||||
utils::hook::detour r_end_frame_hook;
|
||||
utils::hook::detour g_run_frame_hook;
|
||||
utils::hook::detour main_frame_hook;
|
||||
|
||||
|
||||
@ -98,9 +97,9 @@ namespace scheduler
|
||||
r_end_frame_hook.invoke<void>();
|
||||
}
|
||||
|
||||
void server_frame_stub()
|
||||
void g_clear_vehicle_inputs_stub()
|
||||
{
|
||||
g_run_frame_hook.invoke<void>();
|
||||
game::G_ClearVehicleInputs();
|
||||
execute(pipeline::server);
|
||||
}
|
||||
|
||||
@ -168,12 +167,14 @@ namespace scheduler
|
||||
{
|
||||
if (!game::is_server())
|
||||
{
|
||||
r_end_frame_hook.create(0x142272B00_g, r_end_frame_stub);
|
||||
// some func called before R_EndFrame, maybe SND_EndFrame?
|
||||
r_end_frame_hook.create(0x142272B00_g, r_end_frame_stub);
|
||||
}
|
||||
|
||||
main_frame_hook.create(game::select(0x1420F8E00, 0x1405020E0), main_frame_stub);
|
||||
// Com_Frame_Try_Block_Function
|
||||
main_frame_hook.create(game::select(0x1420F8E00, 0x1405020E0), main_frame_stub);
|
||||
|
||||
utils::hook::call(game::select(0x14225522E, 0x140538427), g_clear_vehicle_inputs_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -13,6 +13,8 @@ namespace script
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour db_findxassetheader_hook;
|
||||
utils::hook::detour gscr_get_bgb_remaining_hook;
|
||||
|
||||
std::unordered_map<std::string, game::RawFile*> loaded_scripts;
|
||||
|
||||
game::RawFile* get_loaded_script(const std::string& name)
|
||||
@ -110,6 +112,11 @@ namespace script
|
||||
|
||||
return asset_header;
|
||||
}
|
||||
|
||||
void gscr_get_bgb_remaining_stub(game::scriptInstance_t inst, void* entref)
|
||||
{
|
||||
game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 255);
|
||||
}
|
||||
}
|
||||
|
||||
struct component final : generic_component
|
||||
@ -126,6 +133,7 @@ namespace script
|
||||
}
|
||||
|
||||
db_findxassetheader_hook.create(game::select(0x141420ED0, 0x1401D5FB0), db_findxassetheader_stub);
|
||||
gscr_get_bgb_remaining_hook.create(game::select(0x141A8CAB0, 0x1402D2310), gscr_get_bgb_remaining_stub);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
@ -14,6 +15,8 @@ namespace server_list
|
||||
{
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour lua_serverinfo_to_table_hook;
|
||||
|
||||
struct state
|
||||
{
|
||||
game::netadr_t address{};
|
||||
@ -73,6 +76,17 @@ namespace server_list
|
||||
|
||||
callback(true, result);
|
||||
}
|
||||
|
||||
void lua_serverinfo_to_table_stub(game::hks::lua_State* state, game::ServerInfo serverInfo, int index)
|
||||
{
|
||||
lua_serverinfo_to_table_hook.invoke(state, serverInfo, index);
|
||||
|
||||
if (state)
|
||||
{
|
||||
auto botCount = atoi(game::Info_ValueForKey(serverInfo.tags, "bots"));
|
||||
game::Lua_SetTableInt("botCount", botCount, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool get_master_server(game::netadr_t& address)
|
||||
@ -132,6 +146,8 @@ namespace server_list
|
||||
s.callback = {};
|
||||
});
|
||||
}, scheduler::async, 200ms);
|
||||
|
||||
lua_serverinfo_to_table_hook.create(0x141F1FD10_g, lua_serverinfo_to_table_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -20,6 +20,7 @@ namespace ui_scripting
|
||||
std::unordered_map<game::hks::cclosure*, std::function<arguments(const function_arguments& args)>>
|
||||
converted_functions;
|
||||
|
||||
utils::hook::detour ui_init_hook;
|
||||
utils::hook::detour ui_cod_init_hook;
|
||||
utils::hook::detour ui_cod_lobbyui_init_hook;
|
||||
utils::hook::detour cl_first_snapshot_hook;
|
||||
@ -192,7 +193,7 @@ namespace ui_scripting
|
||||
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_SECURE;
|
||||
}
|
||||
|
||||
void start()
|
||||
void setup_lua_globals()
|
||||
{
|
||||
globals = {};
|
||||
|
||||
@ -204,6 +205,12 @@ namespace ui_scripting
|
||||
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x141D30290_g)); // hks::base_print
|
||||
lua["table"]["unpack"] = lua["unpack"];
|
||||
lua["luiglobals"] = lua;
|
||||
lua["Engine"]["IsBOIII"] = true;
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
setup_lua_globals();
|
||||
|
||||
const utils::nt::library host{};
|
||||
const auto folder = game::is_server() ? "lobby_scripts/" : "ui_scripts/";
|
||||
@ -223,6 +230,13 @@ namespace ui_scripting
|
||||
}
|
||||
}
|
||||
|
||||
void ui_init_stub(void* allocFunction, void* outOfMemoryFunction)
|
||||
{
|
||||
ui_init_hook.invoke(allocFunction, outOfMemoryFunction);
|
||||
|
||||
setup_lua_globals();
|
||||
}
|
||||
|
||||
bool doneFirstSnapshot = false;
|
||||
|
||||
void ui_cod_init_stub(const bool frontend)
|
||||
@ -467,6 +481,7 @@ namespace ui_scripting
|
||||
return;
|
||||
}
|
||||
|
||||
ui_init_hook.create(0x142704FF0_g, ui_init_stub);
|
||||
cl_first_snapshot_hook.create(0x141320E60_g, cl_first_snapshot_stub);
|
||||
|
||||
scheduler::once([]()
|
||||
|
@ -5,48 +5,39 @@
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace workshop
|
||||
{
|
||||
const std::string get_usermap_publisher_id(const std::string& mapname)
|
||||
{
|
||||
const auto total_usermaps = *reinterpret_cast<unsigned int*>(0x1567B3580_g);
|
||||
|
||||
for (unsigned int i = 0; i < total_usermaps; ++i)
|
||||
{
|
||||
const auto usermap_data = reinterpret_cast<game::workshop_data*>(0x1567B3588_g + (sizeof(game::workshop_data) * i));
|
||||
if (usermap_data->folderName == mapname)
|
||||
{
|
||||
return usermap_data->publisherId;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
bool check_valid_publisher_id(const std::string& mapname, const std::string& pub_id)
|
||||
{
|
||||
if (!game::DB_FileExists(mapname.data(), 0) && pub_id.empty())
|
||||
{
|
||||
game::Com_Error(0, "Can't find usermap: %s!\nMake sure you're subscribed to the workshop item.", mapname.data());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void load_usermap_mod_if_needed(const std::string& pub_id)
|
||||
{
|
||||
if (!game::isModLoaded() && !pub_id.empty())
|
||||
{
|
||||
game::loadMod(0, "usermaps", 0);
|
||||
}
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour setup_server_map_hook;
|
||||
|
||||
bool has_mod(const std::string& pub_id)
|
||||
{
|
||||
const auto total_mods = *reinterpret_cast<unsigned int*>(0x15678D170_g);
|
||||
|
||||
for (unsigned int i = 0; i < total_mods; ++i)
|
||||
{
|
||||
const auto mod_data = reinterpret_cast<game::workshop_data*>(0x15678D178_g + (sizeof(game::workshop_data) * i));
|
||||
if (mod_data->publisherId == pub_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void load_usermap_mod_if_needed(const std::string& publisher_id)
|
||||
{
|
||||
if (!game::isModLoaded() && !publisher_id.empty())
|
||||
{
|
||||
game::loadMod(0, "usermaps", true);
|
||||
}
|
||||
}
|
||||
|
||||
void setup_server_map_stub(int localClientNum, const char* mapname, const char* gametype)
|
||||
{
|
||||
const auto publisher_id = get_usermap_publisher_id(mapname);
|
||||
@ -72,6 +63,102 @@ namespace workshop
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_mod_name(const std::string& mod_id)
|
||||
{
|
||||
if (mod_id == "usermaps" || !game::is_server())
|
||||
{
|
||||
return mod_id;
|
||||
}
|
||||
|
||||
const utils::nt::library host{};
|
||||
const auto base_path = host.get_folder().generic_string();
|
||||
const auto path = utils::string::va("%s/mods/%s/zone/workshop.json", base_path.data(), mod_id.data());
|
||||
const auto json_str = utils::io::read_file(path);
|
||||
|
||||
if (json_str.empty())
|
||||
{
|
||||
printf("[ Workshop ] workshop.json has not been found in mod folder: %s\n", mod_id.data());
|
||||
return mod_id;
|
||||
}
|
||||
|
||||
rapidjson::Document doc;
|
||||
const rapidjson::ParseResult parse_result = doc.Parse(json_str);
|
||||
|
||||
if (parse_result.IsError() || !doc.IsObject())
|
||||
{
|
||||
printf("[ Workshop ] Unable to parse workshop.json\n");
|
||||
return mod_id;
|
||||
}
|
||||
|
||||
if (doc.HasMember("Title"))
|
||||
{
|
||||
std::string title = doc["Title"].GetString();
|
||||
|
||||
if (title.size() > 31)
|
||||
{
|
||||
title.resize(31);
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
printf("[ Workshop ] workshop.json has no \"Title\" member.\n");
|
||||
return mod_id;
|
||||
}
|
||||
|
||||
std::string get_usermap_publisher_id(const std::string& mapname)
|
||||
{
|
||||
const auto total_usermaps = *reinterpret_cast<unsigned int*>(0x1567B3580_g);
|
||||
|
||||
for (unsigned int i = 0; i < total_usermaps; ++i)
|
||||
{
|
||||
const auto usermap_data = reinterpret_cast<game::workshop_data*>(0x1567B3588_g + (sizeof(game::workshop_data) * i));
|
||||
if (usermap_data->folderName == mapname)
|
||||
{
|
||||
return usermap_data->publisherId;
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool check_valid_usermap_id(const std::string& mapname, const std::string& pub_id)
|
||||
{
|
||||
if (!game::DB_FileExists(mapname.data(), 0) && pub_id.empty())
|
||||
{
|
||||
game::UI_OpenErrorPopupWithMessage(0, 0x100,
|
||||
utils::string::va("Can't find usermap: %s!\nMake sure you're subscribed to the workshop item.", mapname.data()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_valid_mod_id(const std::string& mod)
|
||||
{
|
||||
if (mod.empty() || mod == "usermaps")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!has_mod(mod))
|
||||
{
|
||||
game::UI_OpenErrorPopupWithMessage(0, 0x100,
|
||||
utils::string::va("Can't find mod with publisher id: %s!\nMake sure you're subscribed to the workshop item.", mod.data()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void load_mod_if_needed(const std::string& usermap, const std::string& mod)
|
||||
{
|
||||
if (!usermap.empty() || mod != "usermaps")
|
||||
{
|
||||
game::loadMod(0, mod.data(), true);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public client_component
|
||||
{
|
||||
public:
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
namespace workshop
|
||||
{
|
||||
const std::string get_usermap_publisher_id(const std::string& mapname);
|
||||
bool check_valid_publisher_id(const std::string& mapname, const std::string& pub_id);
|
||||
void load_usermap_mod_if_needed(const std::string& pub_id);
|
||||
std::string get_usermap_publisher_id(const std::string& mapname);
|
||||
std::string get_mod_name(const std::string& mod_id);
|
||||
bool check_valid_usermap_id(const std::string& mapname, const std::string& pub_id);
|
||||
bool check_valid_mod_id(const std::string& pub_id);
|
||||
void load_mod_if_needed(const std::string& usermap, const std::string& mod);
|
||||
}
|
||||
|
155
src/client/game/fragment_handler.cpp
Normal file
155
src/client/game/fragment_handler.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
#include <std_include.hpp>
|
||||
#include "fragment_handler.hpp"
|
||||
|
||||
namespace game::fragment_handler
|
||||
{
|
||||
namespace
|
||||
{
|
||||
constexpr size_t MAX_FRAGMENTS = 100;
|
||||
|
||||
using fragments = std::unordered_map<size_t, std::string>;
|
||||
|
||||
struct fragmented_packet
|
||||
{
|
||||
size_t fragment_count{0};
|
||||
fragments fragments{};
|
||||
std::chrono::high_resolution_clock::time_point insertion_time = std::chrono::high_resolution_clock::now();
|
||||
};
|
||||
|
||||
using id_fragment_map = std::unordered_map<uint64_t, fragmented_packet>;
|
||||
using address_fragment_map = std::unordered_map<netadr_t, id_fragment_map>;
|
||||
|
||||
utils::concurrency::container<address_fragment_map> global_map{};
|
||||
|
||||
std::vector<std::string> construct_fragments(const void* data, const size_t length)
|
||||
{
|
||||
std::vector<std::string> fragments{};
|
||||
|
||||
constexpr size_t max_fragment_size = 0x400;
|
||||
|
||||
for (size_t i = 0; i < length; i += max_fragment_size)
|
||||
{
|
||||
const auto current_fragment_size = std::min(length - i, max_fragment_size);
|
||||
|
||||
std::string fragment(static_cast<const char*>(data) + i, current_fragment_size);
|
||||
fragments.push_back(std::move(fragment));
|
||||
}
|
||||
|
||||
return fragments;
|
||||
}
|
||||
}
|
||||
|
||||
bool handle(const netadr_t& target, utils::byte_buffer& buffer, std::string& final_packet)
|
||||
{
|
||||
const auto fragment_id = buffer.read<uint64_t>();
|
||||
const size_t fragment_count = buffer.read<uint32_t>();
|
||||
const size_t fragment_index = buffer.read<uint32_t>();
|
||||
|
||||
auto fragment_data = buffer.get_remaining_data();
|
||||
|
||||
if (fragment_index > fragment_count || !fragment_count || fragment_count > MAX_FRAGMENTS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return global_map.access<bool>([&](address_fragment_map& map)
|
||||
{
|
||||
auto& user_map = map[target];
|
||||
if (!user_map.contains(fragment_id) && user_map.size() > MAX_FRAGMENTS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& packet_queue = user_map[fragment_id];
|
||||
|
||||
if (packet_queue.fragment_count == 0)
|
||||
{
|
||||
packet_queue.fragment_count = fragment_count;
|
||||
}
|
||||
|
||||
if (packet_queue.fragment_count != fragment_count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (packet_queue.fragments.size() + 1 < fragment_count)
|
||||
{
|
||||
packet_queue.fragments[fragment_index] = std::move(fragment_data);
|
||||
return false;
|
||||
}
|
||||
|
||||
final_packet.clear();
|
||||
|
||||
for (size_t i = 0; i < fragment_count; ++i)
|
||||
{
|
||||
if (i == fragment_index)
|
||||
{
|
||||
final_packet.append(fragment_data);
|
||||
}
|
||||
else
|
||||
{
|
||||
final_packet.append(packet_queue.fragments.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void clean()
|
||||
{
|
||||
global_map.access([](address_fragment_map& map)
|
||||
{
|
||||
for (auto i = map.begin(); i != map.end();)
|
||||
{
|
||||
auto& user_map = i->second;
|
||||
|
||||
for (auto j = user_map.begin(); j != user_map.end();)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - j->second.insertion_time;
|
||||
|
||||
if (diff > 5s)
|
||||
{
|
||||
j = user_map.erase(j);
|
||||
}
|
||||
else
|
||||
{
|
||||
++j;
|
||||
}
|
||||
}
|
||||
|
||||
if (user_map.empty())
|
||||
{
|
||||
i = map.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void fragment_handler::fragment_data(const void* data, const size_t size,
|
||||
const std::function<void(const utils::byte_buffer& buffer)>& callback)
|
||||
{
|
||||
static std::atomic_uint64_t current_id{0};
|
||||
const auto id = current_id++;
|
||||
|
||||
const auto fragments = construct_fragments(data, size);
|
||||
|
||||
for (size_t i = 0; i < fragments.size(); ++i)
|
||||
{
|
||||
utils::byte_buffer buffer{};
|
||||
buffer.write(id);
|
||||
buffer.write(static_cast<uint32_t>(fragments.size()));
|
||||
buffer.write(static_cast<uint32_t>(i));
|
||||
|
||||
auto& fragment = fragments.at(i);
|
||||
buffer.write(fragment.data(), fragment.size());
|
||||
|
||||
callback(buffer);
|
||||
}
|
||||
}
|
||||
}
|
17
src/client/game/fragment_handler.hpp
Normal file
17
src/client/game/fragment_handler.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <utils/byte_buffer.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
#include "../component/network.hpp"
|
||||
|
||||
namespace game::fragment_handler
|
||||
{
|
||||
bool handle(const netadr_t& target, utils::byte_buffer& buffer,
|
||||
std::string& final_packet);
|
||||
|
||||
void clean();
|
||||
|
||||
void fragment_data(const void* data, const size_t size,
|
||||
const std::function<void(const utils::byte_buffer& buffer)>& callback);
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define PROTOCOL 1
|
||||
#define PROTOCOL 3
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace game
|
||||
@ -689,6 +689,7 @@ namespace game
|
||||
byte color[4];
|
||||
const dvar_t* indirect[3];
|
||||
} value;
|
||||
|
||||
uint64_t encryptedValue;
|
||||
};
|
||||
|
||||
@ -699,26 +700,31 @@ namespace game
|
||||
int stringCount;
|
||||
const char** strings;
|
||||
} enumeration;
|
||||
|
||||
struct
|
||||
{
|
||||
int min;
|
||||
int max;
|
||||
} integer;
|
||||
|
||||
struct
|
||||
{
|
||||
int64_t min;
|
||||
int64_t max;
|
||||
} integer64;
|
||||
|
||||
struct
|
||||
{
|
||||
uint64_t min;
|
||||
uint64_t max;
|
||||
} unsignedInt64;
|
||||
|
||||
struct
|
||||
{
|
||||
float min;
|
||||
float max;
|
||||
} value;
|
||||
|
||||
struct
|
||||
{
|
||||
vec_t min;
|
||||
@ -1028,7 +1034,33 @@ namespace game
|
||||
JoinResult joinResult;
|
||||
};
|
||||
|
||||
struct ServerInfo
|
||||
{
|
||||
uint16_t m_usConnectionPort;
|
||||
uint16_t m_usQueryPort;
|
||||
uint32_t m_unIP;
|
||||
int m_nPing;
|
||||
byte unk[0x22];
|
||||
char mapname[32];
|
||||
char description[64];
|
||||
char gamemode[16];
|
||||
char modname[32];
|
||||
int playerCount;
|
||||
int maxPlayers;
|
||||
int unk2;
|
||||
int unk3;
|
||||
int unk4;
|
||||
bool dedicated;
|
||||
bool ranked;
|
||||
bool hardcore;
|
||||
bool zombies;
|
||||
char servername[64];
|
||||
char tags[128];
|
||||
int unk5;
|
||||
int unk6;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace hks
|
||||
{
|
||||
struct lua_State;
|
||||
@ -1051,7 +1083,7 @@ namespace game
|
||||
|
||||
typedef size_t hksSize;
|
||||
typedef void* (*lua_Alloc)(void*, void*, size_t, size_t);
|
||||
typedef hksInt32(*lua_CFunction)(lua_State*);
|
||||
typedef hksInt32 (*lua_CFunction)(lua_State*);
|
||||
|
||||
struct GenericChunkHeader
|
||||
{
|
||||
@ -1108,11 +1140,14 @@ namespace game
|
||||
TNUMBER = 0x3,
|
||||
TSTRING = 0x4,
|
||||
TTABLE = 0x5,
|
||||
TFUNCTION = 0x6, // idk
|
||||
TFUNCTION = 0x6,
|
||||
// idk
|
||||
TUSERDATA = 0x7,
|
||||
TTHREAD = 0x8,
|
||||
TIFUNCTION = 0x9, // Lua function
|
||||
TCFUNCTION = 0xA, // C function
|
||||
TIFUNCTION = 0x9,
|
||||
// Lua function
|
||||
TCFUNCTION = 0xA,
|
||||
// C function
|
||||
TUI64 = 0xB,
|
||||
TSTRUCT = 0xC,
|
||||
NUM_TYPE_OBJECTS = 0xE,
|
||||
@ -1294,7 +1329,7 @@ namespace game
|
||||
int _m_isHksGlobalMemoTestingMode;
|
||||
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
|
||||
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
|
||||
int(*m_debugMap)(const char*, int);
|
||||
int (*m_debugMap)(const char*, int);
|
||||
};
|
||||
|
||||
enum HksBytecodeSharingMode : __int64
|
||||
@ -1504,7 +1539,7 @@ namespace game
|
||||
void* m_profiler;
|
||||
RuntimeProfileData m_runProfilerData;
|
||||
HksCompilerSettings m_compilerSettings;
|
||||
int(*m_panicFunction)(lua_State*);
|
||||
int (*m_panicFunction)(lua_State*);
|
||||
void* m_luaplusObjectList;
|
||||
int m_heapAssertionFrequency;
|
||||
int m_heapAssertionCount;
|
||||
@ -1533,6 +1568,7 @@ namespace game
|
||||
HksError m_error;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef uint32_t ScrVarCanonicalName_t;
|
||||
|
||||
@ -1544,15 +1580,44 @@ namespace game
|
||||
|
||||
struct client_s
|
||||
{
|
||||
char __pad0[0xBB354];
|
||||
int xuid;
|
||||
char __pad1[0x8];
|
||||
int client_state;
|
||||
char __pad0[0x28];
|
||||
netadr_t address;
|
||||
char __pad1[20468];
|
||||
int reliableSequence;
|
||||
int reliableAcknowledge;
|
||||
char __pad2[4];
|
||||
int messageAcknowledge;
|
||||
char gap_5040[1416];
|
||||
uint64_t xuid;
|
||||
char __pad3[0xB5D84];
|
||||
int guid;
|
||||
char __pad4[0x8];
|
||||
bool bIsTestClient;
|
||||
char __pad2[0x29DAC];
|
||||
char __pad5[3];
|
||||
int serverId;
|
||||
char __pad6[171432];
|
||||
};
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(sizeof(client_s) == 0xE5110);
|
||||
|
||||
static_assert(offsetof(game::client_s, address) == 0x2C);
|
||||
static_assert(offsetof(game::client_s, xuid) == 0x55C8);
|
||||
static_assert(offsetof(game::client_s, guid) == 0xBB354);
|
||||
static_assert(offsetof(game::client_s, bIsTestClient) == 0xBB360);
|
||||
#endif
|
||||
|
||||
struct client_s_cl : client_s
|
||||
{
|
||||
char __pad1_0[0x60];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(sizeof(client_s_cl) == 0xE5170);
|
||||
#endif
|
||||
|
||||
enum scriptInstance_t
|
||||
{
|
||||
SCRIPTINSTANCE_SERVER = 0x0,
|
||||
@ -1581,7 +1646,9 @@ namespace game
|
||||
unsigned char __pad1[0x2A0];
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(sizeof(gentity_s) == 0x4F8);
|
||||
#endif
|
||||
|
||||
enum workshop_type
|
||||
{
|
||||
@ -1606,7 +1673,9 @@ namespace game
|
||||
workshop_type type;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
static_assert(sizeof(workshop_data) == 0x4C8);
|
||||
#endif
|
||||
|
||||
struct DDLMember
|
||||
{
|
||||
@ -1678,7 +1747,7 @@ namespace game
|
||||
};
|
||||
|
||||
struct DDLContext;
|
||||
typedef void(* DDLWriteCB)(DDLContext*, void*);
|
||||
typedef void (* DDLWriteCB)(DDLContext*, void*);
|
||||
|
||||
struct DDLContext
|
||||
{
|
||||
|
@ -31,10 +31,14 @@ namespace game
|
||||
WEAK symbol<void(const char* gametype, bool loadDefaultSettings)> Com_GametypeSettings_SetGametype{
|
||||
0x1420F5980
|
||||
};
|
||||
WEAK symbol<unsigned int(const char* settingName, bool getDefault)> Com_GametypeSettings_GetUInt{
|
||||
0x1420F4E00, 0x1404FE5C0
|
||||
};
|
||||
WEAK symbol<bool()> Com_IsRunningUILevel{0x142148350};
|
||||
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
||||
0x14214A4D0
|
||||
};
|
||||
WEAK symbol<const char*(const char* fullpath)> Com_LoadRawTextFile{0x1420F61B0};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1420EC010, 0x1404F75B0};
|
||||
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{
|
||||
@ -71,13 +75,22 @@ namespace game
|
||||
WEAK symbol<bool(const char* zoneName, int source)> DB_FileExists{0x141420B40};
|
||||
WEAK symbol<void()> DB_ReleaseXAssets{0x1414247C0};
|
||||
|
||||
// G
|
||||
WEAK symbol<void()> G_ClearVehicleInputs{0x1423812E0, 0x1405C1200};
|
||||
|
||||
WEAK symbol<qboolean(void* ent)> StuckInClient{0x1415A8360, 0x14023BFE0};
|
||||
|
||||
// Live
|
||||
WEAK symbol<bool(uint64_t, int*, bool)> Live_GetConnectivityInformation{0x141E0C380};
|
||||
|
||||
// Info
|
||||
WEAK symbol<const char*(const char*, const char* key)> Info_ValueForKey{0x1422E87B0};
|
||||
|
||||
// MSG
|
||||
WEAK symbol<uint8_t(msg_t* msg)> MSG_ReadByte{0x142155450, 0x14050D1B0};
|
||||
|
||||
// NET
|
||||
WEAK symbol<bool(netsrc_t sock, netadr_t* adr, const void* data, int len)> NET_OutOfBandData{0x142173600};
|
||||
WEAK symbol<bool(netsrc_t sock, int length, const void* data, const netadr_t* to)> NET_SendPacket{
|
||||
0x1423323B0, 0x140596E40
|
||||
};
|
||||
@ -93,7 +106,7 @@ namespace game
|
||||
WEAK symbol<const char*(const char* name)> CopyString{0x1422AC220, 0x14056BD70};
|
||||
|
||||
WEAK symbol<bool()> isModLoaded{0x1420D5020};
|
||||
WEAK symbol<void(int, const char*, int)> loadMod{0x1420D6930};
|
||||
WEAK symbol<void(int, const char*, bool)> loadMod{0x1420D6930};
|
||||
|
||||
// Dvar
|
||||
WEAK symbol<bool(const dvar_t* dvar)> Dvar_IsSessionModeBaseDvar{0x1422C23A0, 0x140576890};
|
||||
@ -105,10 +118,15 @@ namespace game
|
||||
WEAK symbol<const char*(const dvar_t* dvar)> Dvar_DisplayableValue{0x1422BC080};
|
||||
WEAK symbol<bool(const dvar_t* dvar)> Dvar_GetBool{0x1422BCED0};
|
||||
WEAK symbol<int(const dvar_t* dvar)> Dvar_GetInt{0x0, 0x140575C20};
|
||||
WEAK symbol<float(const dvar_t* dvar)> Dvar_GetFLoat{0x0, 0x140575B20};
|
||||
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, bool value, int flags,
|
||||
const char* description)> Dvar_RegisterBool{
|
||||
0x1422D0900, 0x14057B500
|
||||
};
|
||||
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, float value, float min, float max, unsigned int flags,
|
||||
const char* description)> Dvar_RegisterFloat{
|
||||
0x0, 0x14057B6B0
|
||||
};
|
||||
WEAK symbol<dvar_t*(dvarStrHash_t hash, const char* dvarName, bool value, int flags,
|
||||
const char* description)> Dvar_SessionModeRegisterBool{
|
||||
0x1422D0D40, 0x14057BAA0
|
||||
@ -129,6 +147,7 @@ namespace game
|
||||
};
|
||||
|
||||
// UI
|
||||
WEAK symbol<void(int localClientNumber, int errorcode, const char* errorMessage)> UI_OpenErrorPopupWithMessage{0x14228DEE0};
|
||||
WEAK symbol<void(bool frontend)> UI_CoD_Init{0x141F29010, 0x1404A0A50};
|
||||
WEAK symbol<void()> UI_CoD_LobbyUI_Init{0x141F2BD80, 0x1404A1F50};
|
||||
WEAK symbol<void()> UI_CoD_Shutdown{0x141F32E10, 0x0};
|
||||
@ -137,9 +156,10 @@ namespace game
|
||||
WEAK symbol<void(hks::lua_State*, const char*)> Lua_CoD_LoadLuaFile{0x141F11A20, 0x0};
|
||||
WEAK symbol<void(int localClientNum)> CG_LUIHUDRestart{0x140F7E970};
|
||||
WEAK symbol<void(int localClientNum)> CL_CheckKeepDrawingConnectScreen{0x1413CCAE0};
|
||||
WEAK symbol<void(const char* key, int value, hks::lua_State* luaVM)> Lua_SetTableInt{0x141F066E0};
|
||||
|
||||
// Scr
|
||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x0, 0x14016F160};
|
||||
WEAK symbol<void(scriptInstance_t inst, int value)> Scr_AddInt{0x1412E9870, 0x14016F160};
|
||||
WEAK symbol<void(scriptInstance_t inst, const char* value)> Scr_AddString{0x0, 0x14016F320};
|
||||
WEAK symbol<const char*(scriptInstance_t inst, unsigned int index)> Scr_GetString{0x0, 0x140171490};
|
||||
WEAK symbol<void(gentity_s* ent, ScrVarCanonicalName_t stringValue, unsigned int paramcount)> Scr_Notify_Canon{
|
||||
@ -157,6 +177,9 @@ namespace game
|
||||
0x141CD98D0
|
||||
};
|
||||
|
||||
// PCache
|
||||
WEAK symbol<void(ControllerIndex_t controllerIndex)> PCache_DeleteEntries{0x141E8D710};
|
||||
|
||||
// SV
|
||||
WEAK symbol<bool()> SV_Loaded{0x142252250, 0x140535460};
|
||||
WEAK symbol<void*()> SV_AddTestClient{0x142248F40, 0x14052E3E0};
|
||||
@ -172,6 +195,9 @@ namespace game
|
||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0};
|
||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670};
|
||||
|
||||
// FS
|
||||
WEAK symbol<char*(int bytes)> FS_AllocMem{0x1422AC9F0, 0x14056C340};
|
||||
|
||||
// Utils
|
||||
WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
|
||||
|
||||
@ -190,7 +216,11 @@ namespace game
|
||||
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
||||
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
||||
|
||||
WEAK symbol<client_s> svs_clients{0x0, 0x14A178E98};
|
||||
WEAK symbol<int> fs_loadStack{0x157A65310, 0x14A39C650};
|
||||
|
||||
// Client and dedi struct size differs :(
|
||||
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
||||
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
||||
|
||||
// Dvar variables
|
||||
WEAK symbol<dvar_t*> com_maxclients{0x0, 0x14948EE70};
|
||||
|
@ -45,7 +45,8 @@ namespace game
|
||||
return dvar->current.value.enabled;
|
||||
}
|
||||
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const int flags, const char* description, const eModes mode)
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, const bool value, const int flags,
|
||||
const char* description, const eModes mode)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_SessionModeRegisterBool(hash, dvar_name, value, flags, description);
|
||||
@ -83,7 +84,22 @@ namespace game
|
||||
return registered_dvar;
|
||||
}
|
||||
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const int flags, const char* description)
|
||||
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags,
|
||||
const char* description)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_RegisterFloat(hash, dvar_name, value, min, max, flags, description);
|
||||
|
||||
if (registered_dvar)
|
||||
{
|
||||
registered_dvar->debugName = dvar_name;
|
||||
}
|
||||
|
||||
return registered_dvar;
|
||||
}
|
||||
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, const int flags,
|
||||
const char* description)
|
||||
{
|
||||
const auto hash = Dvar_GenerateHash(dvar_name);
|
||||
auto* registered_dvar = Dvar_RegisterString(hash, dvar_name, value, flags, description);
|
||||
@ -135,4 +151,101 @@ namespace game
|
||||
|
||||
dvar_to_change->flags = flags;
|
||||
}
|
||||
|
||||
bool is_server_running()
|
||||
{
|
||||
return get_dvar_bool("sv_running");
|
||||
}
|
||||
|
||||
size_t get_max_client_count()
|
||||
{
|
||||
return static_cast<size_t>(get_dvar_int("com_maxclients"));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void foreach_client(T* client_states, const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
if (!client_states || !callback)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < get_max_client_count(); ++i)
|
||||
{
|
||||
callback(client_states[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static bool access_client(T* client_states, const size_t index, const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
if (!client_states || !callback)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index >= get_max_client_count())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& client = client_states[index];
|
||||
if (client.client_state <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
callback(client);
|
||||
return true;
|
||||
}
|
||||
|
||||
void foreach_client(const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
if (is_server())
|
||||
{
|
||||
foreach_client(*svs_clients, callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach_client(*svs_clients_cl, callback);
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_client(const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
foreach_client([&](client_s& client, size_t)
|
||||
{
|
||||
callback(client);
|
||||
});
|
||||
}
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&, size_t index)>& callback)
|
||||
{
|
||||
foreach_client([&](client_s& client, const size_t index)
|
||||
{
|
||||
if (client.client_state > 0)
|
||||
{
|
||||
callback(client, index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
foreach_connected_client([&](client_s& client, size_t)
|
||||
{
|
||||
callback(client);
|
||||
});
|
||||
}
|
||||
|
||||
bool access_connected_client(const size_t index, const std::function<void(client_s&)>& callback)
|
||||
{
|
||||
if (is_server())
|
||||
{
|
||||
return access_client(*svs_clients, index, callback);
|
||||
}
|
||||
|
||||
return access_client(*svs_clients_cl, index, callback);
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,21 @@ namespace game
|
||||
bool get_dvar_bool(const char* dvar_name);
|
||||
|
||||
const dvar_t* register_dvar_bool(const char* dvar_name, bool value, int flags, const char* description);
|
||||
const dvar_t* register_dvar_float(const char* dvar_name, float value, float min, float max, const int flags, const char* description);
|
||||
const dvar_t* register_sessionmode_dvar_bool(const char* dvar_name, bool value, int flags, const char* description, eModes mode = MODE_COUNT);
|
||||
const dvar_t* register_dvar_string(const char* dvar_name, const char* value, int flags, const char* description);
|
||||
|
||||
void dvar_add_flags(const char* dvar, dvarFlags_e flags);
|
||||
void dvar_set_flags(const char* dvar_name, dvarFlags_e flags);
|
||||
|
||||
bool is_server_running();
|
||||
size_t get_max_client_count();
|
||||
|
||||
void foreach_client(const std::function<void(client_s&, size_t index)>& callback);
|
||||
void foreach_client(const std::function<void(client_s&)>& callback);
|
||||
|
||||
void foreach_connected_client(const std::function<void(client_s&, size_t index)>& callback);
|
||||
void foreach_connected_client(const std::function<void(client_s&)>& callback);
|
||||
|
||||
bool access_connected_client(size_t index, const std::function<void(client_s&)>& callback);
|
||||
}
|
||||
|
@ -1,9 +1,28 @@
|
||||
#include <std_include.hpp>
|
||||
#include "window.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
thread_local uint32_t window_count = 0;
|
||||
|
||||
uint32_t get_dpi_for_window(const HWND window)
|
||||
{
|
||||
const utils::nt::library user32{"user32.dll"};
|
||||
const auto get_dpi = user32 ? user32.get_proc<UINT(WINAPI *)(HWND)>("GetDpiForWindow") : nullptr;
|
||||
|
||||
if (!get_dpi)
|
||||
{
|
||||
return USER_DEFAULT_SCREEN_DPI;
|
||||
}
|
||||
|
||||
return get_dpi(window);
|
||||
}
|
||||
}
|
||||
|
||||
window::window(const std::string& title, const int width, const int height,
|
||||
@ -34,6 +53,10 @@ window::window(const std::string& title, const int width, const int height,
|
||||
this->handle_ = CreateWindowExA(NULL, this->wc_.lpszClassName, title.data(), flags, x, y, width, height, nullptr,
|
||||
nullptr, this->wc_.hInstance, this);
|
||||
|
||||
BOOL value = TRUE;
|
||||
DwmSetWindowAttribute(this->handle_,
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value));
|
||||
|
||||
SendMessageA(this->handle_, WM_DPICHANGED, 0, 0);
|
||||
ShowWindow(this->handle_, SW_SHOW);
|
||||
SetForegroundWindow(this->handle_);
|
||||
@ -67,7 +90,7 @@ LRESULT window::processor(const UINT message, const WPARAM w_param, const LPARAM
|
||||
{
|
||||
if (message == WM_DPICHANGED)
|
||||
{
|
||||
const auto dpi = GetDpiForWindow(*this);
|
||||
const auto dpi = get_dpi_for_window(*this);
|
||||
if (dpi != this->last_dpi_)
|
||||
{
|
||||
RECT rect;
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#pragma comment (lib, "dwmapi.lib")
|
||||
|
||||
class window
|
||||
{
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <atlsafe.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <wincrypt.h>
|
||||
#include <dwmapi.h>
|
||||
#include <shellscalingapi.h>
|
||||
#include <d3d11.h>
|
||||
#include <dxgi1_6.h>
|
||||
@ -64,6 +65,7 @@
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
@ -55,10 +55,14 @@ namespace steam
|
||||
const auto mode = game::eModes(std::atoi(playmode.data()));
|
||||
|
||||
const auto* tags = ::utils::string::va(
|
||||
R"(\gametype\%s\dedicated\%s\ranked\false\hardcore\false\zombies\%s\modName\\playerCount\%d)",
|
||||
R"(\gametype\%s\dedicated\%s\ranked\false\hardcore\%s\zombies\%s\playerCount\%d\bots\%d\modName\%s\)",
|
||||
info.get("gametype").data(),
|
||||
info.get("dedicated") == "1" ? "true" : "false",
|
||||
mode == game::MODE_ZOMBIES ? "true" : "false", server.m_nPlayers);
|
||||
info.get("hc") == "1" ? "true" : "false",
|
||||
mode == game::MODE_ZOMBIES ? "true" : "false",
|
||||
server.m_nPlayers,
|
||||
atoi(info.get("bots").data()),
|
||||
info.get("modname").data());
|
||||
|
||||
::utils::string::copy(server.m_szGameTags, tags);
|
||||
server.m_steamID.bits = strtoull(info.get("xuid").data(), nullptr, 16);
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "file_updater.hpp"
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/http.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/compression.hpp>
|
||||
@ -271,7 +272,7 @@ namespace updater
|
||||
bool file_updater::is_outdated_file(const file_info& file) const
|
||||
{
|
||||
#if !defined(NDEBUG) || !defined(CI)
|
||||
if (file.name == UPDATE_HOST_BINARY)
|
||||
if (file.name == UPDATE_HOST_BINARY && !utils::flags::has_flag("update"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
53
src/common/utils/byte_buffer.cpp
Normal file
53
src/common/utils/byte_buffer.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "byte_buffer.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
byte_buffer::byte_buffer()
|
||||
: writing_(true)
|
||||
{
|
||||
}
|
||||
|
||||
byte_buffer::byte_buffer(std::string buffer)
|
||||
: writing_(false)
|
||||
, buffer_(std::move(buffer))
|
||||
{
|
||||
}
|
||||
|
||||
void byte_buffer::write(const void* buffer, const size_t length)
|
||||
{
|
||||
if (!this->writing_)
|
||||
{
|
||||
throw std::runtime_error("Writing to readable byte buffer");
|
||||
}
|
||||
|
||||
this->buffer_.append(static_cast<const char*>(buffer), length);
|
||||
}
|
||||
|
||||
void byte_buffer::read(void* data, const size_t length)
|
||||
{
|
||||
if (this->writing_)
|
||||
{
|
||||
throw std::runtime_error("Reading from writable byte buffer");
|
||||
}
|
||||
|
||||
if (this->offset_ + length > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
memcpy(data, this->buffer_.data() + this->offset_, length);
|
||||
this->offset_ += length;
|
||||
}
|
||||
|
||||
std::string byte_buffer::read_data(const size_t length)
|
||||
{
|
||||
std::string result{};
|
||||
result.resize(length);
|
||||
|
||||
this->read(result.data(), result.size());
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
141
src/common/utils/byte_buffer.hpp
Normal file
141
src/common/utils/byte_buffer.hpp
Normal file
@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
class byte_buffer
|
||||
{
|
||||
public:
|
||||
byte_buffer();
|
||||
byte_buffer(std::string buffer);
|
||||
|
||||
template <typename T>
|
||||
byte_buffer(const std::basic_string_view<T>& buffer)
|
||||
: byte_buffer(std::string(reinterpret_cast<const char*>(buffer.data()), buffer.size() * sizeof(T)))
|
||||
{
|
||||
}
|
||||
|
||||
void write(const void* buffer, size_t length);
|
||||
|
||||
void write(const char* text)
|
||||
{
|
||||
this->write(text, strlen(text));
|
||||
}
|
||||
|
||||
void write_string(const char* str, const size_t length)
|
||||
{
|
||||
this->write<uint32_t>(static_cast<uint32_t>(length));
|
||||
this->write(str, length);
|
||||
}
|
||||
|
||||
void write_string(const std::string& str)
|
||||
{
|
||||
this->write_string(str.data(), str.size());
|
||||
}
|
||||
|
||||
void write_string(const char* str)
|
||||
{
|
||||
this->write_string(str, strlen(str));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const T& object)
|
||||
{
|
||||
this->write(&object, sizeof(object));
|
||||
}
|
||||
|
||||
template<>
|
||||
void write<byte_buffer>(const byte_buffer& object)
|
||||
{
|
||||
const auto& buffer = object.get_buffer();
|
||||
this->write(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write(const std::vector<T>& vec)
|
||||
{
|
||||
this->write(vec.data(), vec.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void write_vector(const std::vector<T>& vec)
|
||||
{
|
||||
this->write(static_cast<uint32_t>(vec.size()));
|
||||
this->write(vec);
|
||||
}
|
||||
|
||||
const std::string& get_buffer() const
|
||||
{
|
||||
return this->buffer_;
|
||||
}
|
||||
|
||||
std::string move_buffer()
|
||||
{
|
||||
return std::move(this->buffer_);
|
||||
}
|
||||
|
||||
void read(void* data, size_t length);
|
||||
|
||||
template <typename T>
|
||||
T read()
|
||||
{
|
||||
T object{};
|
||||
this->read(&object, sizeof(object));
|
||||
return object;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> read_vector()
|
||||
{
|
||||
std::vector<T> result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
const auto totalSize = size * sizeof(T);
|
||||
|
||||
if (this->offset_ + totalSize > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), totalSize);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string read_string()
|
||||
{
|
||||
std::string result{};
|
||||
const auto size = this->read<uint32_t>();
|
||||
|
||||
if (this->offset_ + size > this->buffer_.size())
|
||||
{
|
||||
throw std::runtime_error("Out of bounds read from byte buffer");
|
||||
}
|
||||
|
||||
result.resize(size);
|
||||
this->read(result.data(), size);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t get_remaining_size() const
|
||||
{
|
||||
return this->buffer_.size() - offset_;
|
||||
}
|
||||
|
||||
std::string get_remaining_data()
|
||||
{
|
||||
return this->read_data(this->get_remaining_size());
|
||||
}
|
||||
|
||||
std::string read_data(size_t length);
|
||||
|
||||
private:
|
||||
bool writing_{false};
|
||||
size_t offset_{0};
|
||||
std::string buffer_{};
|
||||
};
|
||||
}
|
@ -45,6 +45,11 @@ namespace utils::concurrency
|
||||
return object_;
|
||||
}
|
||||
|
||||
std::unique_lock<MutexType> acquire_lock()
|
||||
{
|
||||
return std::unique_lock<MutexType>{mutex_};
|
||||
}
|
||||
|
||||
private:
|
||||
mutable MutexType mutex_{};
|
||||
T object_{};
|
||||
|
@ -8,6 +8,11 @@ namespace utils
|
||||
this->parse(buffer);
|
||||
}
|
||||
|
||||
info_string::info_string(const char* buffer)
|
||||
: info_string(std::string{buffer})
|
||||
{
|
||||
}
|
||||
|
||||
info_string::info_string(const std::string_view& buffer)
|
||||
: info_string(std::string{buffer})
|
||||
{
|
||||
|
@ -9,8 +9,9 @@ namespace utils
|
||||
{
|
||||
public:
|
||||
info_string() = default;
|
||||
info_string(const std::string& buffer);
|
||||
info_string(const std::string_view& buffer);
|
||||
explicit info_string(const std::string& buffer);
|
||||
explicit info_string(const char* buffer);
|
||||
explicit info_string(const std::string_view& buffer);
|
||||
info_string(const std::basic_string_view<uint8_t>& buffer);
|
||||
|
||||
void set(const std::string& key, const std::string& value);
|
||||
|
@ -194,18 +194,19 @@ namespace utils::io
|
||||
|
||||
std::vector<std::filesystem::path> list_files(const std::filesystem::path& directory, const bool recursive)
|
||||
{
|
||||
std::error_code code{};
|
||||
std::vector<std::filesystem::path> files;
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory))
|
||||
for (auto& file : std::filesystem::recursive_directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& file : std::filesystem::directory_iterator(directory))
|
||||
for (auto& file : std::filesystem::directory_iterator(directory, code))
|
||||
{
|
||||
files.push_back(file.path());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user