14#include <networkbasetypes.pb.h>
15#include <networksystem/inetworkmessages.h>
16#include <networksystem/netmessage.h>
18#include <tier1/utlstring.h>
52 RemoveHooksIfUnused();
83 for (
const Kick&
kick : std::exchange(_kicks, {}))
89 "Required workshop addon download was declined");
94Status WorkshopDownloads::Install()
102 if (!_engine || !_engine->IsDedicatedServer())
104 return std::unexpected(
Error::Unsupported(
"addon downloads need a dedicated server"));
108 return std::unexpected(
Error::Unsupported(
"the client SteamID or server addons offset did not bind"));
128 _joinMessageHook = std::move(*
join);
129 _connectionReplyHook = std::move(*
reply);
133void WorkshopDownloads::RemoveHooksIfUnused()
141 _joinMessageHook.
Reset();
142 _connectionReplyHook.
Reset();
143 _addedToReply.clear();
153 const char*
reported = server.GetAddonName();
157 Log::Error(
"Addons: the CNetworkGameServer::m_szAddons offset {} is stale; not mounting addons.",
186 if (_addedToReply.empty())
197 if (_addedToReply.empty())
206 _addedToReply.clear();
212 _addedToReply.clear();
240 Log::Info(
"Addons: a reconnect message named {} addons; sending {} and holding the rest.",
251 _kicks.push_back({.Slot =
slot, .SteamId =
steamId});
Typed read/write of engine fields at a base pointer + byte offset.
AddonDecision DecideJoinMessage(int64_t steamId, bool reconnect, std::string_view addons, double now, int maxAttempts)
bool HasMissing(int64_t steamId) const
void ClientReconnected(int64_t steamId, double now, double timeoutSec)
std::vector< uint64_t > ClientMountList(int64_t steamId) const
void ClientLeft(int64_t steamId, double now, double forgetAfterSec)
T Read(const void *base) const
static bool IsValid(int64_t steamId64)
static double MonotonicSeconds()
void OnClientDisconnected(int64_t steamId)
WorkshopDownloads(const Bindings &bindings, const EngineInterfaces &engine)
void Remove(uint64_t addonId) override
bool IsReady(int slot) override
bool Add(uint64_t addonId) override
void OnClientConnected(int64_t steamId)
void Error(std::format_string< Args... > fmt, Args &&... args)
void Info(std::format_string< Args... > fmt, Args &&... args)
void Warn(std::format_string< Args... > fmt, Args &&... args)
static constexpr double ForgetAfterSeconds
Result< Subscription > HookVirtual(std::string_view name, const VirtualFn< Ret(Object *, Args...)> &function, Before &&before, After &&after=nullptr)
Hook a gamedata-bound virtual function on objects sharing its class vtable.
Result< Subscription > HookFunction(std::string_view name, const Fn< Ret(Object *, Args...)> &function, Before &&before, After &&after=nullptr)
Hook a signature-bound function at its entry.
int ClientSlot(const Bindings &bindings, const void *client)
static constexpr double DownloadTimeoutSeconds
@ Engine
The engine or SDK refused, returned nothing, or is not where it was expected.
static std::string ReadFile(const std::filesystem::path &path)
std::vector< uint64_t > AppendToAddonList(std::string &field, const std::vector< uint64_t > &ids)
constexpr bool IsValidSlot(int slot)
void RemoveFromAddonList(std::string &field, const std::vector< uint64_t > &ids)
static constexpr int MaxDownloadAttempts
std::expected< void, Error > Status
@ TrimToFirst
the message names several addons; keep only AddonDecision::Id
@ Kick
the client refused AddonDecision::Id too often
@ Unchanged
send the message as it is
@ Send
point the client at AddonDecision::Id and wait for its reconnect
@ KeepMounted
a map change names no addon; name AddonDecision::Id so the client keeps it
static CUtlString * AddonList(const Bindings &bindings, CNetworkGameServerBase &server)
VirtualFn< bool(EngineClient *, const CNetMessage *, NetChannelBufType_t)> SendNetMessage
Offset< int64_t > ClientSteamId
Offset< void > ServerAddons
Fn< void(CNetworkGameServerBase *, EngineClient *)> ReplyConnection
static Error Unsupported(std::string detail)