Skip to content

Commit

Permalink
Merge branch 'pr-2243'
Browse files Browse the repository at this point in the history
tweak(server): scriptless startup notices (pr-2243):
 - 821e4a9 tweak(server): scriptless startup notices
  • Loading branch information
thorium-cfx committed Oct 25, 2023
2 parents 2ebe4ae + 821e4a9 commit 5570dbd
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 171 deletions.
2 changes: 1 addition & 1 deletion code/components/citizen-server-impl/component.lua
Expand Up @@ -11,7 +11,7 @@ end

return function()
filter {}
add_dependencies { 'vendor:folly', 'vendor:lua' }
add_dependencies { 'vendor:folly' }

removefiles {
'components/citizen-server-impl/src/state/**'
Expand Down
60 changes: 60 additions & 0 deletions code/components/citizen-server-impl/include/NoticeLogicProcessor.h
@@ -0,0 +1,60 @@
#pragma once

#include <StdInc.h>
#include <ServerInstanceBase.h>
#include <ResourceManager.h>

#include <json.hpp>

namespace fx
{
class NoticeLogicProcessor
{
private:
enum RuleOp
{
And,
Or,
Not,
NullOrEmpty,
Contains,
Equals
};

enum RuleType
{
ConVar,
StartedResourceList
};

public:
NoticeLogicProcessor(fx::ServerInstanceBase* server);
bool ProcessNoticeRule(const nlohmann::json& ruleRef, uint32_t nestingLevel) const;

static void BeginProcessingNotices(fx::ServerInstanceBase* server, const nlohmann::json& noticesBlob);

private:
ConsoleVariableManager* m_cvManager;
fx::ResourceManager* m_resManager;

// Macro'd map entries to prevent accidental typos or bad mapping
#define StrEnumMapEntry(EnumName, ValueName) \
{ \
#ValueName, EnumName::ValueName \
}

const std::map<std::string, RuleOp> m_ruleOpStringToEnum{
StrEnumMapEntry(RuleOp, And),
StrEnumMapEntry(RuleOp, Or),
StrEnumMapEntry(RuleOp, Not),
StrEnumMapEntry(RuleOp, NullOrEmpty),
StrEnumMapEntry(RuleOp, Contains),
StrEnumMapEntry(RuleOp, Equals)
};

const std::map<std::string, RuleType> m_ruleTypeStringToEnum{
StrEnumMapEntry(RuleType, ConVar),
StrEnumMapEntry(RuleType, StartedResourceList),
};
};
}
158 changes: 158 additions & 0 deletions code/components/citizen-server-impl/src/NoticeLogicProcessor.cpp
@@ -0,0 +1,158 @@
#include <StdInc.h>
#include "NoticeLogicProcessor.h"

fx::NoticeLogicProcessor::NoticeLogicProcessor(fx::ServerInstanceBase* server)
{
m_cvManager = server->GetComponent<console::Context>()->GetVariableManager();
m_resManager = server->GetComponent<fx::ResourceManager>()->GetCurrent();
}

bool fx::NoticeLogicProcessor::ProcessNoticeRule(const nlohmann::json& ruleRef, uint32_t nestingLevel) const
{
if (nestingLevel >= 10)
{
throw std::invalid_argument("Maximum nesting level for notice rules was exceeded");
}
if (!ruleRef.is_object())
{
return false;
}
auto ruleObject = ruleRef.get<nlohmann::json::object_t>();

auto& opObject = ruleObject["op"];
if (!opObject.is_string())
{
return false;
}

auto mappedOp = m_ruleOpStringToEnum.find(opObject.get<std::string>());
if (mappedOp == m_ruleOpStringToEnum.end())
{
return false;
}

auto opNum = mappedOp->second;
switch (opNum)
{
case RuleOp::And:
case RuleOp::Or:
{
auto& nestedRules = ruleObject["rules"];
if (!nestedRules.is_array())
{
return false;
}

for (auto& subRule : nestedRules)
{
auto isSubRuleTrue = ProcessNoticeRule(subRule, nestingLevel + 1);
if (opNum == RuleOp::And)
{
if (!isSubRuleTrue)
{
return false;
}
}
else // ::Or
{
if (isSubRuleTrue)
{
return true;
}
}
}

// Final catch when all ::Ands were true or all ::Ors were false
return opNum == RuleOp::And ? true : false;
}
case RuleOp::Not:
{
auto& ruleToInvert = ruleObject["rule"];
return ruleToInvert.is_object() ? !ProcessNoticeRule(ruleToInvert, nestingLevel + 1) : false;
}
case RuleOp::NullOrEmpty:
case RuleOp::Contains:
case RuleOp::Equals:
{
auto& typeObject = ruleObject["type"];
if (!typeObject.is_string())
{
return false;
}

auto mappedType = m_ruleTypeStringToEnum.find(typeObject.get<std::string>());
if (mappedType == m_ruleTypeStringToEnum.end())
{
return false;
}

auto typeNum = mappedType->second;
if (typeNum == RuleType::ConVar)
{
auto& keyObject = ruleObject["key"];
if (!keyObject.is_string())
{
return false;
}

auto cvEntry = m_cvManager->FindEntryRaw(keyObject.get<std::string>());

if (opNum == RuleOp::NullOrEmpty)
return !cvEntry || cvEntry->GetValue() == "";

auto& dataObject = ruleObject["data"];
if (!dataObject.is_string())
{
return false;
}

switch (opNum)
{
case RuleOp::Contains:
return cvEntry->GetValue().find(dataObject.get<std::string>()) != std::string::npos;
case RuleOp::Equals:
return cvEntry->GetValue() == dataObject.get<std::string>();
}
}
else if (typeNum == RuleType::StartedResourceList && opNum == RuleOp::Contains)
{
auto& dataObject = ruleObject["data"];
if (!dataObject.is_string())
{
return false;
}

auto resource = m_resManager->GetResource(dataObject.get<std::string>(), false);
return (resource.GetRef() && resource->GetState() == fx::ResourceState::Started);
}
return false;
}
}
return false;
}

void fx::NoticeLogicProcessor::BeginProcessingNotices(fx::ServerInstanceBase* server, const nlohmann::json& noticesBlob)
{
auto nlp = fx::NoticeLogicProcessor::NoticeLogicProcessor(server);

for (auto& [noticeType, data] : noticesBlob.get<nlohmann::json::object_t>())
{
auto& enabled = data["enabled"];
if (!enabled.get<bool>())
continue;

auto& rootRule = data["rule"];
auto ruleIsTrue = nlp.ProcessNoticeRule(rootRule, 0);

if (ruleIsTrue)
{
auto& lines = data["notice_lines"];
if (lines.is_array())
{
trace("^1-- [server notice: %s]^7\n", noticeType);
for (auto& noticeLine : lines)
trace("%s\n", noticeLine.get<std::string>());
}
}
}
}

0 comments on commit 5570dbd

Please sign in to comment.