Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e141a06
translate comments to english, add localemanager, tweak build output
Prefix Jan 5, 2026
5839d53
fix applygamessettings hook
Prefix Jan 5, 2026
98cef8b
translation for all languages
Prefix Jan 5, 2026
3362470
fix usage of convars that is not being initiated on that step
Prefix Jan 5, 2026
c968cf4
fix usage of convars that is not being initiated on that step
Prefix Jan 5, 2026
ae1f443
fix CallAllModulesLoaded
Prefix Jan 5, 2026
0c172dd
tweak calls
Prefix Jan 5, 2026
d5aa888
fix translations, fix init/postinit invocations
Prefix Jan 5, 2026
c5ae09f
tweak formatter
Prefix Jan 5, 2026
d5bf6a5
tweak default prefix
Prefix Jan 5, 2026
23ce204
fix module name
Prefix Jan 5, 2026
4066b29
add _localizerManager to nominate
Prefix Jan 5, 2026
7994f0c
revert changes for adminmanager, commandmanager
Prefix Jan 6, 2026
1eac9d7
UseHook-> InitNativeHooks
Prefix Jan 6, 2026
e73cb3a
revert changes for IAdmin translations
Prefix Jan 6, 2026
70c231d
update interfacebrige translations
Prefix Jan 6, 2026
78b2d69
make convars as readonly
Prefix Jan 6, 2026
823c826
revert GetFileName
Prefix Jan 7, 2026
e6efb8a
Clean up Directory.Build.props by removing conditions
Prefix Jan 7, 2026
abe16f5
Update .gitignore
Prefix Jan 7, 2026
0fa64a4
Fix formatting in Directory.Build.props
Prefix Jan 7, 2026
d93fbcc
Add check to skip fake clients in ExtendService
Prefix Jan 7, 2026
9430445
Filter out fake clients in nomination process
Prefix Jan 7, 2026
9d78d9e
Skip processing for fake clients in RtvService
Prefix Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
498 changes: 498 additions & 0 deletions .asset/game/sharp/locales/Ptr.Modules.MapManager.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@
<PackageReference Include="ModSharp.Sharp.Shared" Version="*" PrivateAssets="all" />
</ItemGroup>

</Project>
</Project>
16 changes: 9 additions & 7 deletions src/Ptr.Modules.MapManager/InterfaceBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public InterfaceBridge(ISharedSystem sharedSystem,
public string ModuleIdentity { get; init; }

/// <summary>
/// 开洞,一般情况下别用!
/// Don't use it under normal circumstances!
/// </summary>
internal static InterfaceBridge Instance { get; private set; } = null!;

Expand All @@ -99,14 +99,16 @@ public InterfaceBridge(ISharedSystem sharedSystem,
public IModSharp ModSharp => _sharedSystem.GetModSharp();

/// <summary>
/// CGlobalVars* gpGlobals,没什么好说的。<br />
/// 注意,一定要在地图加载之后调用!不然服务器第一次加载的时候是拿不到的!
/// CGlobalVars* gpGlobals,There's not much to say。<br />
/// Note that this must be called after the map is loaded! <br />
/// Otherwise, it won't be available when the server loads it for the first time!
/// </summary>
public IGlobalVars GlobalVars => ModSharp.GetGlobals();

/// <summary>
/// CGameRules* g_pGameRules <br />
/// 注意,一定要在地图加载之后调用!不然服务器第一次加载的时候是拿不到的!
/// CGameRules* g_pGameRules <br />
/// Note that this must be called after the map is loaded! <br />
/// Otherwise, it won't be available when the server loads it for the first time!
/// </summary>
public IGameRules GameRules => ModSharp.GetGameRules();

Expand Down Expand Up @@ -137,9 +139,9 @@ public InterfaceBridge(ISharedSystem sharedSystem,
public DateTime AllowVoteTime { get; set; }

/// <summary>
/// 开洞,一般情况下别用!
/// Don't use it under normal circumstances!
/// </summary>
public string CurrentMapGroup { get; set; } = string.Empty;

//public ICommandRegistry CommandRegistry { get; set; } = null!;
}
}
59 changes: 45 additions & 14 deletions src/Ptr.Modules.MapManager/MapManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
using Ptr.Modules.MapManager.Hooks;
using Ptr.Modules.MapManager.Services;
using Ptr.Modules.MapManager.Shared;
using Ptr.Shared.Extensions;
using Ptr.Shared.Hooks.Hosting;
using Ptr.Shared.Hooks.Managers;
using Ptr.Shared.Hosting;
using Ptr.Shared.Misc;
using Sharp.Modules.LocalizerManager.Shared;
using Sharp.Shared;
using Sharp.Shared.Abstractions;
using Sharp.Shared.Listeners;
Expand All @@ -20,10 +20,11 @@ namespace Ptr.Modules.MapManager;
internal class MapManager : IModSharpModule, IMapManager, IGameListener
{
private readonly InterfaceBridge _bridge;
private readonly IConVar? _countdownAfterChangeLevel;
private readonly ILogger<MapManager> _logger;
private readonly IServiceProvider _provider;
private readonly IConVar? _voteSuccessRatio;
private IConVar? _chatFormatPrefix;
private IConVar? _voteSuccessRatio;
private IConVar? _countdownAfterChangeLevel;

public MapManager(
ISharedSystem sharedSystem,
Expand All @@ -34,7 +35,6 @@ public MapManager(
bool hotReload)
{
var formatter = new ChatMessageFormatter();
formatter.SetPrefix("{green}[地图]{white}");
var bridge = new InterfaceBridge(sharedSystem, dllPath, sharpPath, version, configuration, hotReload, formatter,
this);
var services = new ServiceCollection();
Expand All @@ -50,17 +50,10 @@ public MapManager(
services.AddLogging(x => x.ClearProviders());

var provider = services.BuildServiceProvider();
provider.UseHook<ApplyGameSettingsHook>();
_bridge = provider.GetRequiredService<InterfaceBridge>();
_logger = sharedSystem.GetLoggerFactory().CreateLogger<MapManager>();

_provider = provider;

_countdownAfterChangeLevel = _bridge.ConVarManager.CreateConVar("mapmanager_countdown_after_changelevel", 180,
"Countdown after map change (unit is sec.)");

_voteSuccessRatio = _bridge.ConVarManager.CreateConVar("mapmanager_vote_success_ratio", 0.6f,
"Ratio request for a success vote.");
}

internal int GetVoteSuccessNumberRequested()
Expand Down Expand Up @@ -132,19 +125,38 @@ private void CallMapConfigLoaded()
public bool Init()
{
InitConfig();

_provider.LoadAllSharpExtensions();
_provider.InitNativeHooks();
_provider.CallInit<IModule>(e => { _logger.LogError(e, "An error occurred when initializing modules"); });

return true;
}

public void PostInit()
{
// Safest place to init convars is PostInit
_chatFormatPrefix = _bridge.ConVarManager.CreateConVar("mapmanager_format_prefix", "{whitespace}{red}MapVote{whitespace}{muted}",
"Chat prefix format for map manager module.");

// Real time prefix changes
if (_chatFormatPrefix is not null)
{
_bridge.ChatFormatter.SetPrefix(_chatFormatPrefix.GetString());
_bridge.ConVarManager.InstallChangeHook(_chatFormatPrefix, OnChatFormatPrefixChange);
}

_countdownAfterChangeLevel = _bridge.ConVarManager.CreateConVar("mapmanager_countdown_after_changelevel", 180,
"Countdown after map change (unit is sec.)");

_voteSuccessRatio = _bridge.ConVarManager.CreateConVar("mapmanager_vote_success_ratio", 0.6f,
"Ratio request for a success vote.");
_provider.CallPostInit<IModule>(e => { _logger.LogError(e, "An error occurred when initializing modules"); });
_bridge.SharpModuleManager.RegisterSharpModuleInterface<IMapManager>(this, IMapManager.Identity, this);
}

private void OnChatFormatPrefixChange(IConVar conVar)
{
_bridge.ChatFormatter.SetPrefix(conVar.GetString());
}

public void OnLibraryConnected(string name)
{
_provider.CallLibraryConnected<IModule>(name, e =>
Expand All @@ -155,7 +167,17 @@ public void OnLibraryConnected(string name)

public void OnAllModulesLoaded()
{
_provider.LoadAllSharpExtensions();
_provider.UseHook<ApplyGameSettingsHook>();
_provider.InitNativeHooks();
_provider.CallAllModulesLoaded<IModule>(e => { _logger.LogError(e, "An error occurred when initializing modules"); });

CallMapConfigLoaded();

var _localizerManager = _bridge.SharpModuleManager
.GetRequiredSharpModuleInterface<ILocalizerManager>(ILocalizerManager.Identity).Instance!;
_localizerManager.LoadLocaleFile("Ptr.Modules.MapManager");

}

public void Shutdown()
Expand All @@ -165,6 +187,10 @@ public void Shutdown()
_provider.ShutdownAllSharpExtensions();
_bridge.Maps.Clear();
MapConfigLoaded = null;
if (_chatFormatPrefix is not null)
{
_bridge.ConVarManager.RemoveChangeHook(_chatFormatPrefix, OnChatFormatPrefixChange);
}
}

string IModSharpModule.DisplayName => "Ptr.Modules.MapManager";
Expand Down Expand Up @@ -226,6 +252,11 @@ public void ChangeLevel(IGameMap map)
public void OnServerActivate()
{
_bridge.AllowVoteTime = DateTime.Now.AddSeconds(_countdownAfterChangeLevel?.GetInt32() ?? 180);
// On this step we already executed .cfg files
if (_chatFormatPrefix is not null)
{
_bridge.ChatFormatter.SetPrefix(_chatFormatPrefix.GetString());
}
}

int IGameListener.ListenerPriority => 0;
Expand Down
1 change: 1 addition & 0 deletions src/Ptr.Modules.MapManager/Ptr.Modules.MapManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<ProjectReference Include="..\Ptr.Modules.MapManager.Shared\Ptr.Modules.MapManager.Shared.csproj" Private="false" ExcludeAssets="runtime" />
<ProjectReference Include="..\Sharp.Modules.CommandManager.Shared\Sharp.Modules.CommandManager.Shared.csproj" Private="false" ExcludeAssets="runtime" />
<PackageReference Include="ModSharp.Sharp.Modules.LocalizerManager.Shared" Version="2.1.93" PrivateAssets="all"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ptr.Shared\Ptr.Shared.csproj" Private="false" ExcludeAssets="all" />
Expand Down
85 changes: 53 additions & 32 deletions src/Ptr.Modules.MapManager/Services/ExtendService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Ptr.Shared.Extensions;
using Ptr.Shared.Hosting;
using Sharp.Modules.CommandManager.Shared;
using Sharp.Modules.LocalizerManager.Shared;
using Sharp.Shared.Definition;
using Sharp.Shared.Enums;
using Sharp.Shared.Listeners;
Expand All @@ -15,46 +16,60 @@ internal interface IExtendService : IModule;
internal class ExtendService : IExtendService, IClientListener, IGameListener
{
private readonly InterfaceBridge _bridge;
private readonly IConVar? _enableExtend;

private readonly IConVar _enableExtend;
private readonly IConVar _extendTime;
private readonly IConVar _maxExtCount;
private readonly bool[] _extClients = new bool[64];
private readonly IConVar? _extendTime;
private readonly ILogger<ExtendService> _logger;
private readonly IConVar? _maxExtCount;
private int _extCount;
private ICommandRegistry _commandRegistry = null!;
private ILocalizerManager _localizerManager = null!;

public ExtendService(InterfaceBridge bridge, ILogger<ExtendService> logger)
{
_bridge = bridge;
_logger = logger;
_enableExtend = _bridge.ConVarManager.CreateConVar("mapmanager_enable_extend", true, "Enable ext");
_enableExtend = _bridge.ConVarManager.CreateConVar("mapmanager_enable_extend", true, "Enable map extensions command", ConVarFlags.Release)!;
_maxExtCount = _bridge.ConVarManager.CreateConVar("mapmanager_max_extend_count", 3,
"Maximum allowed extend map time limit count.");
_extendTime =
_bridge.ConVarManager.CreateConVar("mapmanager_ext_time", 15, "The extend applies for time limit.");
"Maximum allowed extend map time limit count.", ConVarFlags.Release)!;
_extendTime = _bridge.ConVarManager.CreateConVar("mapmanager_ext_time", 15, "The extend applies for time limit.", ConVarFlags.Release)!;
}

private void OnCommandExt(IGameClient client, StringCommand command)
{
if (_enableExtend.GetBool() is not true)
{
return;
}
if (_localizerManager is null)
{
throw new InvalidOperationException("LocalizerManager is not initialized.");
}
_localizerManager.TryGetLocalizer(client, out var _localizer);
if (_localizer is null)
{
_logger.LogWarning("Localizer not found for client {ClientSlot} when executing ext command.",
client.Slot);
return;
}
var remaining = (int)(_bridge.AllowVoteTime - DateTime.Now).TotalSeconds;
if (remaining > 0)
{
client.PrintToChat(
_bridge.ChatFormatter.Format($"{ChatColor.Green}{remaining}{ChatColor.White} 秒后才能发起延长地图时间投票。"));
_bridge.ChatFormatter.Format(_localizer.Format("ptr.mapmanager.extend_map_after_x", remaining)));
return;
}

var maxAllowed = _maxExtCount!.GetInt32();
var maxAllowed = _maxExtCount.GetInt32();
if (_extCount >= maxAllowed)
{
client.PrintToChat(_bridge.ChatFormatter.Format("已到达最大可延长时间次数。"));
client.PrintToChat(_bridge.ChatFormatter.Format(_localizer.Format("ptr.mapmanager.max_extends_reached")));
return;
}

if (_extClients[client.Slot])
{
client.PrintToChat("你已经投票过延长地图时间了。");
client.PrintToChat(_bridge.ChatFormatter.Format(_localizer.Format("ptr.mapmanager.you_already_voted_to_extend")));
return;
}

Expand All @@ -63,16 +78,31 @@ private void OnCommandExt(IGameClient client, StringCommand command)
var request = _bridge.MapManager.GetVoteSuccessNumberRequested();
var clientGaps = request - current;
client.PrintToChat(
$"已有 {ChatColor.Green}{current}{ChatColor.White} 人投票延长地图时间,还需 {ChatColor.Green}{clientGaps}{ChatColor.White} 票。");
_bridge.ChatFormatter.Format(_localizer.Format("ptr.mapmanager.extend_vote_progress", current, clientGaps)));
if (clientGaps > 0)
{
return;
}

_bridge.ModSharp.PrintToChatAll("投票通过,即将延长地图持续时间。");
var allClients = _bridge.ClientManager.GetGameClients(true);
foreach (var c in allClients)
{
if (c.IsFakeClient)
{
continue;
}
_localizerManager.TryGetLocalizer(client, out var _tempLocalizer);
if (_tempLocalizer is null)
{
_logger.LogWarning("Localizer not found for client {ClientSlot} when executing ext command.",
c.Slot);
continue;
}
c.PrintToChat(
_bridge.ChatFormatter.Format(_tempLocalizer.Format("ptr.mapmanager.extend_vote_passed")));
}
var timeLimit = _bridge.ConVarManager.FindConVar("mp_timelimit")!;
var currentTimeLeft = timeLimit.GetFloat();
var pendingExtendTime = _extendTime?.GetFloat() ?? 15.0f;
var pendingExtendTime = _extendTime.GetFloat();
var nextTimeLeft = currentTimeLeft + pendingExtendTime;
timeLimit.Set($"{nextTimeLeft}");
_extCount++;
Expand All @@ -95,38 +125,29 @@ private void ResetExtCount()
}

#region IModule

public void OnInit()
{
if (_enableExtend?.GetBool() is true)
{
return;
}
_bridge.ModSharp.InstallGameListener(this);
_bridge.ClientManager.InstallClientListener(this);

_logger.LogInformation("Ext is disabled.");
}

public void OnAllModulesLoaded()
{
if (_enableExtend?.GetBool() is not true)
{
return;
}

_commandRegistry = _bridge.SharpModuleManager
.GetRequiredSharpModuleInterface<ICommandManager>(ICommandManager.Identity).Instance!
.GetRegistry(_bridge.ModuleIdentity);

_commandRegistry.RegisterClientCommand("ext", OnCommandExt);

_localizerManager = _bridge.SharpModuleManager
.GetRequiredSharpModuleInterface<ILocalizerManager>(ILocalizerManager.Identity)
.Instance!;
}

public void OnShutdown()
{
if (_enableExtend?.GetBool() is not true)
{
return;
}

ResetExtCount();
ResetClientsExt();
}
Expand Down Expand Up @@ -162,4 +183,4 @@ public void OnGameDeactivate()
int IGameListener.ListenerVersion => IGameListener.ApiVersion;

#endregion
}
}
4 changes: 2 additions & 2 deletions src/Ptr.Modules.MapManager/Services/MapVoteService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private ECommandAction OnEndMatchVoteNextMap(IGameClient client, StringCommand c

private void OnMapVoteCreated(IMapVoteCreatedForwardParams @params)
{
// GetMapGroupMapList的索引是可以直接和EndMatchMapGroupVoteOptions对应的
// The index of GetMapGroupMapList directly corresponds to EndMatchMapGroupVoteOptions
if (_bridge.ModSharp.GetMapGroupMapList(_bridge.CurrentMapGroup) is not { } mapGroupElements)
{
_logger.LogInformation("Current map group {CurrentMapGroup} cannot retrive map list, check your map group configuration!", _bridge.CurrentMapGroup);
Expand Down Expand Up @@ -174,7 +174,7 @@ private List<IGameMap> SelectMapsForVote(int maxMaps = 10)

private void SummaryVote()
{
// GetMapGroupMapList的索引是可以直接和EndMatchMapGroupVoteOptions对应的
// The index of GetMapGroupMapList directly corresponds to EndMatchMapGroupVoteOptions
if (_bridge.ModSharp.GetMapGroupMapList(_bridge.CurrentMapGroup) is not { } mapGroupElements)
{
return;
Expand Down
Loading