Skip to content

Commit d3ee8e0

Browse files
committed
console: server->client convar replication using setr command
1 parent 49495e0 commit d3ee8e0

File tree

7 files changed

+161
-1
lines changed

7 files changed

+161
-1
lines changed

code/client/citicore/console/Console.Variables.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ ConsoleVariableManager::ConsoleVariableManager(console::Context* parentContext)
5555
setCommand(ConVar_ServerInfo, variable, value);
5656
});
5757
#endif
58+
59+
// set replicated
60+
m_setrCommand = std::make_unique<ConsoleCommand>(m_parentContext, "setr", [=](const std::string& variable, const std::string& value) {
61+
setCommand(ConVar_Replicated, variable, value);
62+
});
5863
}
5964

6065
ConsoleVariableManager::~ConsoleVariableManager()
@@ -127,6 +132,20 @@ void ConsoleVariableManager::RemoveEntryFlags(const std::string& name, int flags
127132
}
128133
}
129134

135+
int ConsoleVariableManager::GetEntryFlags(const std::string& name)
136+
{
137+
std::unique_lock<std::shared_mutex> lock(m_mutex);
138+
139+
auto it = m_entries.find(name);
140+
141+
if (it != m_entries.end())
142+
{
143+
return it->second.flags;
144+
}
145+
146+
return 0;
147+
}
148+
130149
void ConsoleVariableManager::ForAllVariables(const TVariableCB& callback, int flagMask)
131150
{
132151
// store first so we don't have to deal with recursive locks
@@ -152,6 +171,29 @@ void ConsoleVariableManager::ForAllVariables(const TVariableCB& callback, int fl
152171
}
153172
}
154173

174+
void ConsoleVariableManager::RemoveVariablesWithFlag(int flagMask)
175+
{
176+
std::vector<int> iterationList;
177+
178+
{
179+
std::unique_lock<std::shared_mutex> lock(m_mutex);
180+
181+
for (auto& entry : m_entries)
182+
{
183+
// if flags match the mask
184+
if ((entry.second.flags & flagMask) != 0)
185+
{
186+
iterationList.push_back(entry.second.token);
187+
}
188+
}
189+
}
190+
191+
for (auto token : iterationList)
192+
{
193+
Unregister(token);
194+
}
195+
}
196+
155197
void ConsoleVariableManager::SaveConfiguration(const TWriteLineCB& writeLineFunction)
156198
{
157199
ForAllVariables([&](const std::string& name, int flags, const THandlerPtr& variable) {

code/client/citicore/console/Console.Variables.h

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "Console.CommandHelpers.h"
44
#include "Console.Commands.h"
55

6+
#include <se/Security.h>
7+
68
namespace internal
79
{
810
class ConsoleVariableEntryBase
@@ -50,6 +52,7 @@ enum ConsoleVariableFlags
5052
ConVar_Archive = 0x1,
5153
ConVar_Modified = 0x2,
5254
ConVar_ServerInfo = 0x4,
55+
ConVar_Replicated = 0x8,
5356
};
5457

5558
class ConsoleVariableManager
@@ -78,8 +81,12 @@ class ConsoleVariableManager
7881

7982
virtual void RemoveEntryFlags(const std::string& name, int flags);
8083

84+
virtual int GetEntryFlags(const std::string& name);
85+
8186
virtual void ForAllVariables(const TVariableCB& callback, int flagMask = 0xFFFFFFFF);
8287

88+
virtual void RemoveVariablesWithFlag(int flags);
89+
8390
virtual void SaveConfiguration(const TWriteLineCB& writeLineFunction);
8491

8592
inline console::Context* GetParentContext()
@@ -116,6 +123,7 @@ class ConsoleVariableManager
116123
std::unique_ptr<ConsoleCommand> m_setCommand;
117124
std::unique_ptr<ConsoleCommand> m_setaCommand;
118125
std::unique_ptr<ConsoleCommand> m_setsCommand;
126+
std::unique_ptr<ConsoleCommand> m_setrCommand;
119127

120128
std::unique_ptr<ConsoleCommand> m_toggleCommand;
121129
std::unique_ptr<ConsoleCommand> m_vstrCommand;
@@ -208,6 +216,17 @@ class ConsoleVariableEntry : public ConsoleVariableEntryBase
208216
return false;
209217
}
210218

219+
#ifndef IS_FXSERVER
220+
if (m_manager->GetEntryFlags(m_name) & ConVar_Replicated)
221+
{
222+
if (!seCheckPrivilege("builtin.setReplicated"))
223+
{
224+
console::Printf("cmd", "Cannot set server-replicated ConVar %s from client console.\n", m_name);
225+
return false;
226+
}
227+
}
228+
#endif
229+
211230
// update modified flags if changed
212231
if (!typename ConsoleArgumentTraits<T>::Equal()(m_curValue, newValue))
213232
{

code/components/citizen-server-impl/include/ClientRegistry.h

+2
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ namespace fx
160160

161161
fwEvent<Client*> OnClientCreated;
162162

163+
fwEvent<Client*> OnConnectedClient;
164+
163165
private:
164166
uint16_t m_hostNetId;
165167

code/components/citizen-server-impl/src/ClientRegistry.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ namespace fx
9191
{
9292
events->TriggerClientEvent("onPlayerJoining", target, otherClient->GetNetId(), otherClient->GetName(), otherClient->GetSlotId());
9393
});
94+
95+
// trigger connection handlers
96+
OnConnectedClient(client.get());
9497
}
9598

9699
std::shared_ptr<fx::Client> ClientRegistry::GetHost()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#include <StdInc.h>
2+
3+
#include <CoreConsole.h>
4+
5+
#include <ClientRegistry.h>
6+
#include <GameServer.h>
7+
8+
#include <ServerInstanceBase.h>
9+
10+
#include <msgpack.hpp>
11+
12+
static InitFunction initFunction([]()
13+
{
14+
fx::ServerInstanceBase::OnServerCreate.Connect([](fx::ServerInstanceBase* instance)
15+
{
16+
auto console = instance->GetComponent<console::Context>();
17+
auto gameServer = instance->GetComponent<fx::GameServer>().GetRef();
18+
auto clientRegistry = instance->GetComponent<fx::ClientRegistry>().GetRef();
19+
20+
auto sendVariableList = [](fx::Client* client, const std::map<std::string, std::string>& list)
21+
{
22+
msgpack::sbuffer buf;
23+
msgpack::packer<msgpack::sbuffer> packer(buf);
24+
25+
packer.pack(list);
26+
27+
// build the target event
28+
net::Buffer outBuffer;
29+
outBuffer.Write(HashRageString("msgConVars"));
30+
31+
// payload
32+
outBuffer.Write(buf.data(), buf.size());
33+
34+
client->SendPacket(0, outBuffer, ENET_PACKET_FLAG_RELIABLE);
35+
};
36+
37+
clientRegistry->OnConnectedClient.Connect([console, sendVariableList](fx::Client* client)
38+
{
39+
std::map<std::string, std::string> variableList;
40+
41+
console->GetVariableManager()->ForAllVariables([&variableList](const std::string& name, int flags, const std::shared_ptr<internal::ConsoleVariableEntryBase>& var)
42+
{
43+
variableList.emplace(name, var->GetValue());
44+
}, ConVar_Replicated);
45+
46+
sendVariableList(client, variableList);
47+
});
48+
49+
gameServer->OnTick.Connect([=]()
50+
{
51+
std::map<std::string, std::string> variableList;
52+
53+
console->GetVariableManager()->ForAllVariables([console, &variableList](const std::string& name, int flags, const std::shared_ptr<internal::ConsoleVariableEntryBase>& var)
54+
{
55+
variableList.emplace(name, var->GetValue());
56+
57+
console->GetVariableManager()->RemoveEntryFlags(name, ConVar_Modified);
58+
}, ConVar_Replicated | ConVar_Modified);
59+
60+
if (!variableList.empty())
61+
{
62+
clientRegistry->ForAllClients([&variableList, sendVariableList](const std::shared_ptr<fx::Client>& client)
63+
{
64+
sendVariableList(client.get(), variableList);
65+
});
66+
}
67+
});
68+
}, 999999);
69+
});

code/components/glue/component.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"gta:core",
99
"conhost",
1010
"vendor:cpp-uri",
11-
"vendor:nng"
11+
"vendor:nng",
12+
"vendor:msgpack-c"
1213
],
1314
"provides": []
1415
}

code/components/glue/src/BindNetLibrary.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,16 @@
1212
#include <GameInit.h>
1313
#include <nutsnbolts.h>
1414

15+
#include <msgpack.hpp>
16+
17+
#include <CoreConsole.h>
18+
#include <se/Security.h>
19+
1520
#ifdef GTA_FIVE
1621
static InitFunction initFunction([] ()
1722
{
23+
seGetCurrentContext()->AddAccessControlEntry(se::Principal{ "system.internal" }, se::Object{ "builtin" }, se::AccessType::Allow);
24+
1825
NetLibrary::OnNetLibraryCreate.Connect([] (NetLibrary* library)
1926
{
2027
static NetLibrary* netLibrary = library;
@@ -54,7 +61,24 @@ static InitFunction initFunction([] ()
5461
OnKillNetworkDone.Connect([=]()
5562
{
5663
library->FinalizeDisconnect();
64+
65+
console::GetDefaultContext()->GetVariableManager()->RemoveVariablesWithFlag(ConVar_Replicated);
5766
});
67+
68+
library->AddReliableHandler("msgConVars", [](const char* buf, size_t len)
69+
{
70+
auto unpacked = msgpack::unpack(buf, len);
71+
auto conVars = unpacked.get().as<std::map<std::string, std::string>>();
72+
73+
se::ScopedPrincipal principalScope(se::Principal{ "system.console" });
74+
se::ScopedPrincipal principalScopeInternal(se::Principal{ "system.internal" });
75+
76+
// set variable
77+
for (const auto& conVar : conVars)
78+
{
79+
console::GetDefaultContext()->ExecuteSingleCommandDirect(ProgramArguments{ "setr", conVar.first, conVar.second });
80+
}
81+
}, true);
5882
});
5983

6084
Instance<ICoreGameInit>::Get()->OnGameRequestLoad.Connect([]()

0 commit comments

Comments
 (0)