Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
7 changes: 7 additions & 0 deletions PlugifyProfiler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.vs/
.idea/
bin/
obj/
*.dll
*.pdb
*.json
53 changes: 53 additions & 0 deletions PlugifyProfiler/Convars.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using s2sdk;
using static s2sdk.s2sdk;

namespace PlugifyProfiler;

public partial class PlugifyProfiler
{
internal ConVar? PlayerSpeedHasHostage;
public static int ConvarActionMultiplier = 1024;

public void FindConvar()
{
profilerService.StartRecording("Find ConVar");
PlayerSpeedHasHostage = ConVar.Find("sv_cs_player_speed_has_hostage");
profilerService.StopRecording("Find ConVar");
}

public void ReadConvar()
{
profilerService.StartRecording($"Read ConVar ({ConvarActionMultiplier} times)");
for (int i = 0; i < ConvarActionMultiplier; i++)
{
var value = PlayerSpeedHasHostage?.GetValue();
}
profilerService.StopRecording($"Read ConVar ({ConvarActionMultiplier} times)");
}

public void WriteConvar()
{
profilerService.StartRecording($"Write ConVar ({ConvarActionMultiplier} times)");

for (int i = 0; i < ConvarActionMultiplier; i++)
{
PlayerSpeedHasHostage!.SetFloat(0.5f + (i % 10) * 0.1f, false, false);
}

profilerService.StopRecording($"Write ConVar ({ConvarActionMultiplier} times)");
}

public void ReplicateConvarToClient(int playerid)
{
profilerService.StartRecording($"Replicate ConVar to Client ({ConvarActionMultiplier} times)");

for (int i = 0; i < ConvarActionMultiplier; i++)
{
PlayerSpeedHasHostage!.SendValue(playerid, Random.Shared.NextDouble().ToString());
}

profilerService.StopRecording($"Replicate ConVar to Client ({ConvarActionMultiplier} times)");

}
}
131 changes: 131 additions & 0 deletions PlugifyProfiler/Entitites.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using s2sdk;
using static s2sdk.s2sdk;

namespace PlugifyProfiler;

public unsafe partial class PlugifyProfiler
{
public List<int> Entities = new();

public void CreateEntities()
{
profilerService.StartRecording($"Create {MaxEntities} entities");
for (int i = 0; i < MaxEntities; i++)
{
Entities.Add(CreateEntityByName("point_worldtext")!);
}
profilerService.StopRecording($"Create {MaxEntities} entities");
}

public void SpawnEntities()
{
profilerService.StartRecording($"Spawn {Entities.Count} entities");
foreach (var entity in Entities)
{
DispatchSpawn(entity);
}
profilerService.StopRecording($"Spawn {Entities.Count} entities");
}

public void SchemaWriteEntities()
{
profilerService.StartRecording($"Schema Write + Update ({Entities.Count} entities)");
int index = 0;
foreach (var entity in Entities)
{
var text = $"Performance Test Entity #{index}{Random.Shared.NextInt64()}";
SetEntSchemaString(entity, "CPointWorldText", "m_messageText", text, true, 0);
var color = Color.FromArgb(255, 255, 0, 0).ToRgba();
SetEntSchema(entity, "CPointWorldText", "m_Color", color, true, 0);
index++;
}
profilerService.StopRecording($"Schema Write + Update ({Entities.Count} entities)");
}

public void SchemaReadEntities()
{
profilerService.StartRecording($"Schema Read ({Entities.Count} entities)");
foreach (var entity in Entities)
{
var message = GetEntSchemaString(entity, "CPointWorldText", "m_messageText", 0);
var color = ColorExtensions.FromRgba((int)GetEntSchema(entity, "CPointWorldText", "m_Color", 0));
var enabled = GetEntSchema(entity, "CPointWorldText", "m_bEnabled", 0);
var testColor = Color.FromArgb(255, Math.Clamp(color.R * 2, 0, 255), Math.Clamp(color.G * 2, 0, 255), Math.Clamp(color.B * 2, 0, 255));
}
profilerService.StopRecording($"Schema Read ({Entities.Count} entities)");
}

public void VirtualCallEntities()
{
profilerService.StartRecording($"Virtual Calls (teleport {Entities.Count} entities)");
int index = 0;
foreach (var entity in Entities)
{
TeleportEntity(entity, new Vector3(0, index * 10, 0), Vector3.Zero, Vector3.Zero);
index++;
}
profilerService.StopRecording($"Virtual Calls (teleport {Entities.Count} entities)");
}

public void GetGameRules()
{
profilerService.StartRecording("Get Game Rules");

var gameRules = FindEntityByClassname(-1, "cs_gamerules");
Comment thread
qubka marked this conversation as resolved.
Outdated

profilerService.StopRecording("Get Game Rules");
}

public void DespawnEntity(int entity)
{
profilerService.StartRecording($"Despawn entity (1 entity)");
RemoveEntity(entity);
profilerService.StopRecording($"Despawn entity (1 entity)");
}

public void TeleportAllGameEntities()
{
var i = GetFirstActiveEntity();
var convertedEntities = new List<int>();
for (; i != -1; i = GetNextActiveEntity(i))
{
convertedEntities.Add(i);
}

profilerService.StartRecording("Teleport All Game Entities");

foreach (var entity in convertedEntities)
{
if (GetEntityClassname(entity) != "cs_player_controller")
{
TeleportEntity(entity, new Vector3(Random.Shared.Next(-1000, 1000), Random.Shared.Next(-1000, 1000), Random.Shared.Next(0, 500)), Vector3.Zero, Vector3.Zero);
}
}

profilerService.StopRecording("Teleport All Game Entities");
}
}

public static class ColorExtensions
{
public static int ToRgba(this Color c)
{
return (c.A << 24) |
(c.R << 16) |
(c.G << 8) |
c.B;
}

public static Color FromRgba(int v)
{
byte a = (byte)((v >> 24) & 0xFF);
byte r = (byte)((v >> 16) & 0xFF);
byte g = (byte)((v >> 8) & 0xFF);
byte b = (byte)(v & 0xFF);
return Color.FromArgb(a, r, g, b);
}
}
21 changes: 21 additions & 0 deletions PlugifyProfiler/GameEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using s2sdk;

namespace PlugifyProfiler;

public partial class PlugifyProfiler
{
public void FireGameEventsToPlayer()
{
profilerService.StartRecording($"Fire Game Event to Players");

var e = new EventInfo("show_survival_respawn_status", true);

e.SetString("loc_token", "some message");
e.SetInt("duration", 1);
e.SetInt("userid", 0);

e.FireToClient(0);

profilerService.StopRecording($"Fire Game Event to Players");
}
}
45 changes: 45 additions & 0 deletions PlugifyProfiler/NetMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using s2sdk;

namespace PlugifyProfiler;

public partial class PlugifyProfiler
{
public static int NetMessageMultiplier = 1024;

public void SendNetMessages()
{
profilerService.StartRecording($"Send NetMessage to All Players");

var message = new UserMessage(120);
message.SetFloat("frequency", 1.0f);
message.AddAllPlayers();
message.Send();

profilerService.StopRecording($"Send NetMessage to All Players");
}

internal void CMsgSosStartSoundEventHandler(UserMessage msg)
{
profilerService.StartRecording($"CMsgSosStartSoundEvent Add All Recipients (Server sends NetMessage, {NetMessageMultiplier} times)");

for (int i = 0; i < NetMessageMultiplier; i++)
msg.AddAllPlayers();

profilerService.StopRecording($"CMsgSosStartSoundEvent Add All Recipients (Server sends NetMessage, {NetMessageMultiplier} times)");
}

// public void CCLCMsg_MoveHandler(CCLCMsg_Move msg, int playerid)
// {
// Core.Profiler.StartRecording($"CCLCMsg_Move Simulate Data (Server receives NetMessage, {NetMessageMultiplier} times)");

// for (int i = 0; i < NetMessageMultiplier; i++)
// {
// var data = BitConverter.ToString(msg.Data); // convert bytes to string
// _ = data.Length * playerid;
// }

// Core.Profiler.StopRecording($"CCLCMsg_Move Simulate Data (Server receives NetMessage, {NetMessageMultiplier} times)");
// }

// this is disabled due to not being implemented yet in CounterStrikeSharp
}
119 changes: 119 additions & 0 deletions PlugifyProfiler/PlugifyProfiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.IO;
using Plugify;
using s2sdk;
using static s2sdk.s2sdk;

namespace PlugifyProfiler;

public unsafe partial class PlugifyProfiler : Plugin
{
public static int MaxEntities = 1024;
public static int AddBots = 30; // by default there are 1 bot and 1 player spawned
public static int BotQuota = 32;
public bool IsTestInProgress = false;
public float StartMemory = 0.0f;
public float EndMemory = 0.0f;

internal ConVar? BotQuotaConVar;

private ProfileService profilerService = new();

public void OnPluginStart()
{
profilerService.Enable();

OnEntityCreated_Register((entity) =>
{
if (!IsTestInProgress) return;

DespawnEntity(entity);
});

OnGameFrame_Register((simulating, firstTick, lastTick) =>
{
if (!IsTestInProgress) return;

GetGameRules();
TeleportAllGameEntities();

FindConvar();
ReadConvar();
WriteConvar();

var players = GetMaxClients();
for (int i = 1; i <= players; i++)
{
ReplicateConvarToClient(i);
}

FireGameEventsToPlayer();

SendNetMessages();
});

var flags = ConVarFlag.LinkedConcommand | ConVarFlag.Release | ConVarFlag.ClientCanExecute;
AddConsoleCommand("preparetest", "", flags, PrepareTestCommand, HookMode.Post);
AddConsoleCommand("savetest", "", flags, SaveTestCommand, HookMode.Post);
AddConsoleCommand("starttest", "", flags, StartTestCommand, HookMode.Post);
AddConsoleCommand("endtest", "", flags, EndTestCommand, HookMode.Post);

HookUserMessage(208, (msg) =>
{
if (!IsTestInProgress) return ResultType.Continue;
CMsgSosStartSoundEventHandler(new UserMessage(msg));
return ResultType.Continue;
}, HookMode.Post);
}

public ResultType PrepareTestCommand(int caller, CommandCallingContext context, string[] arguments)
{
var cvar = ConVar.Find("bot_quota");
cvar.SetValue(BotQuota.ToString(), false, false);

for (int i = 0; i < AddBots; i++)
{
ServerCommand("bot_add");
}

Console.WriteLine($"Added {AddBots} bots for profiling test. Current bot quota is {cvar.GetValue()}. Total player count: {GetMaxClients()}");
return ResultType.Continue;
}

public ResultType SaveTestCommand(int caller, CommandCallingContext context, string[] arguments)
{
var json = profilerService.GenerateJSONPerformance("");
File.WriteAllText("profiler_output.json", json);
Console.WriteLine("Profiler data saved to profiler_output.json");
return ResultType.Continue;
}

public ResultType StartTestCommand(int caller, CommandCallingContext context, string[] arguments)
{
StartMemory = GC.GetTotalMemory(true) / (1024.0f * 1024.0f);
IsTestInProgress = true;

CreateEntities();
SpawnEntities();

SchemaWriteEntities();
SchemaReadEntities();
VirtualCallEntities();
return ResultType.Continue;
}

public ResultType EndTestCommand(int caller, CommandCallingContext context, string[] arguments)
{
IsTestInProgress = false;

EndMemory = GC.GetTotalMemory(false) / (1024.0f * 1024.0f);
GC.Collect();
float MemoryAfterGC = GC.GetTotalMemory(false) / (1024.0f * 1024.0f);

Console.WriteLine("=== Profiler Test Memory Usage ===");
Console.WriteLine($"Memory Before Test: {StartMemory} MB");
Console.WriteLine($"Memory After Test: {EndMemory} MB");
Console.WriteLine($"Memory After GC: {MemoryAfterGC} MB");
return ResultType.Continue;
}
}
Loading