mirror of
https://github.com/creazy231/cs2-css-flashlight.git
synced 2026-09-27 20:17:04 +02:00
Merge pull request #9 from creazy231/cursor/flashlight-modernization-net10
Modernize flashlight for CSS/.NET 10 + team config
This commit is contained in:
commit
2083fa5a80
8 changed files with 73 additions and 212 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
||||||
.idea
|
.idea
|
||||||
**/bin/
|
**/bin/
|
||||||
**/obj/
|
**/obj/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
docs/
|
||||||
|
|
|
||||||
|
|
@ -121,4 +121,32 @@ public class FlashlightLogicTests
|
||||||
Assert.Equal("axis_of_intent", config.AttachmentName);
|
Assert.Equal("axis_of_intent", config.AttachmentName);
|
||||||
Assert.Equal("materials/effects/lightcookies/flashlight.vtex", config.LightCookie);
|
Assert.Equal("materials/effects/lightcookies/flashlight.vtex", config.LightCookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("Any", 2, true)]
|
||||||
|
[InlineData("Any", 3, true)]
|
||||||
|
[InlineData("Any", 1, true)]
|
||||||
|
[InlineData("T", 2, true)]
|
||||||
|
[InlineData("T", 3, false)]
|
||||||
|
[InlineData("CT", 3, true)]
|
||||||
|
[InlineData("CT", 2, false)]
|
||||||
|
public void IsTeamAllowed_RespectsConfiguredSide(string allowedTeam, byte team, bool expected)
|
||||||
|
{
|
||||||
|
Assert.Equal(expected, FlashlightLogic.IsTeamAllowed(allowedTeam, team));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("t", "T")]
|
||||||
|
[InlineData("Terrorist", "T")]
|
||||||
|
[InlineData("ct", "CT")]
|
||||||
|
[InlineData("CounterTerrorist", "CT")]
|
||||||
|
[InlineData("any", "Any")]
|
||||||
|
[InlineData("something-else", "Any")]
|
||||||
|
[InlineData(null, "Any")]
|
||||||
|
public void ConfigClamp_NormalizesAllowedTeam(string? input, string expected)
|
||||||
|
{
|
||||||
|
var config = new FlashlightConfig { AllowedTeam = input! };
|
||||||
|
config.Clamp();
|
||||||
|
Assert.Equal(expected, config.AllowedTeam);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ public class FlashlightPlugin : BasePlugin, IPluginConfig<FlashlightConfig>
|
||||||
public override string ModuleAuthor => "creazy.eth";
|
public override string ModuleAuthor => "creazy.eth";
|
||||||
public override string ModuleName => "Flashlight";
|
public override string ModuleName => "Flashlight";
|
||||||
public override string ModuleDescription => "Flashlight for Counter-Strike 2";
|
public override string ModuleDescription => "Flashlight for Counter-Strike 2";
|
||||||
public override string ModuleVersion => "0.1.0";
|
public override string ModuleVersion => "0.1.1";
|
||||||
|
|
||||||
public FlashlightConfig Config { get; set; } = new();
|
public FlashlightConfig Config { get; set; } = new();
|
||||||
|
|
||||||
|
|
@ -169,6 +169,12 @@ public class FlashlightPlugin : BasePlugin, IPluginConfig<FlashlightConfig>
|
||||||
|
|
||||||
private void TryToggleFlashlight(CCSPlayerController player, PlayerFlashlightState state)
|
private void TryToggleFlashlight(CCSPlayerController player, PlayerFlashlightState state)
|
||||||
{
|
{
|
||||||
|
// Turning on is restricted by AllowedTeam; turning off is always allowed.
|
||||||
|
if (!state.IsOn && !FlashlightLogic.IsTeamAllowed(Config.AllowedTeam, (byte)player.Team))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var isOn = state.IsOn;
|
var isOn = state.IsOn;
|
||||||
var canToggle = state.CanToggle;
|
var canToggle = state.CanToggle;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,12 @@ public class FlashlightConfig : BasePluginConfig
|
||||||
[JsonPropertyName("AllowUseKey")]
|
[JsonPropertyName("AllowUseKey")]
|
||||||
public bool AllowUseKey { get; set; } = true;
|
public bool AllowUseKey { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Which team may use the flashlight: Any, CT, or T.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("AllowedTeam")]
|
||||||
|
public string AllowedTeam { get; set; } = "Any";
|
||||||
|
|
||||||
[JsonPropertyName("ToggleCooldownSeconds")]
|
[JsonPropertyName("ToggleCooldownSeconds")]
|
||||||
public float ToggleCooldownSeconds { get; set; } = 0.25f;
|
public float ToggleCooldownSeconds { get; set; } = 0.25f;
|
||||||
|
|
||||||
|
|
@ -97,5 +103,17 @@ public class FlashlightConfig : BasePluginConfig
|
||||||
{
|
{
|
||||||
LightCookie = "materials/effects/lightcookies/flashlight.vtex";
|
LightCookie = "materials/effects/lightcookies/flashlight.vtex";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AllowedTeam = NormalizeAllowedTeam(AllowedTeam);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string NormalizeAllowedTeam(string? value)
|
||||||
|
{
|
||||||
|
return value?.Trim().ToUpperInvariant() switch
|
||||||
|
{
|
||||||
|
"T" or "TERRORIST" or "TERRORISTS" => "T",
|
||||||
|
"CT" or "COUNTERTERRORIST" or "COUNTERTERRORISTS" or "COUNTER-TERRORIST" or "COUNTER-TERRORISTS" => "CT",
|
||||||
|
_ => "Any"
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,4 +59,18 @@ public static class FlashlightLogic
|
||||||
{
|
{
|
||||||
return !isOn && hasValidLight;
|
return !isOn && hasValidLight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether <paramref name="team"/> may use the flashlight.
|
||||||
|
/// Team values match CS2: 2 = Terrorist, 3 = Counter-Terrorist.
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsTeamAllowed(string allowedTeam, byte team)
|
||||||
|
{
|
||||||
|
return allowedTeam switch
|
||||||
|
{
|
||||||
|
"T" => team == 2,
|
||||||
|
"CT" => team == 3,
|
||||||
|
_ => true
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ Flashlight is a Counter-Strike 2 server plugin written in C# with [CounterStrike
|
||||||
- Toggle with the Use key (`E` by default) or `css_fl_toggle`
|
- Toggle with the Use key (`E` by default) or `css_fl_toggle`
|
||||||
- One `light_barn` per player, parented to the pawn attachment (no per-tick spawn/teleport)
|
- One `light_barn` per player, parented to the pawn attachment (no per-tick spawn/teleport)
|
||||||
- Configurable brightness, range, color, shadows, offsets, and attachment
|
- Configurable brightness, range, color, shadows, offsets, and attachment
|
||||||
|
- Optional team restriction (`Any`, `CT`, or `T`)
|
||||||
- Automatically turns off on death, spawn, and team change
|
- Automatically turns off on death, spawn, and team change
|
||||||
- Bots ignored
|
- Bots ignored
|
||||||
|
|
||||||
|
|
@ -38,6 +39,7 @@ On first load, CounterStrikeSharp writes a JSON config for the plugin. Defaults:
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `Enabled` | `true` | Master switch |
|
| `Enabled` | `true` | Master switch |
|
||||||
| `AllowUseKey` | `true` | Allow Use-key toggle |
|
| `AllowUseKey` | `true` | Allow Use-key toggle |
|
||||||
|
| `AllowedTeam` | `Any` | Who may use it: `Any`, `CT`, or `T` |
|
||||||
| `ToggleCooldownSeconds` | `0.25` | Toggle cooldown |
|
| `ToggleCooldownSeconds` | `0.25` | Toggle cooldown |
|
||||||
| `Brightness` | `1.0` | Light brightness |
|
| `Brightness` | `1.0` | Light brightness |
|
||||||
| `Range` | `2048` | Light range |
|
| `Range` | `2048` | Light range |
|
||||||
|
|
@ -77,11 +79,12 @@ Unit tests cover toggle/cooldown logic, Use-key edge detection, origin math, cre
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
### v0.1.0
|
### v0.1.1
|
||||||
|
|
||||||
- Updated to .NET 10 and CounterStrikeSharp.API 1.0.371
|
- Updated to .NET 10 and CounterStrikeSharp.API 1.0.371
|
||||||
- Replaced per-tick `light_omni2` spawn/teleport with parented `light_barn`
|
- Replaced per-tick `light_omni2` spawn/teleport with parented `light_barn`
|
||||||
- Added `IPluginConfig` settings for light and toggle behavior
|
- Added `IPluginConfig` settings for light and toggle behavior
|
||||||
|
- Added `AllowedTeam` config (`Any` / `CT` / `T`) to restrict flashlight by side
|
||||||
- Added focused xUnit tests for pure helpers
|
- Added focused xUnit tests for pure helpers
|
||||||
- Updated GitHub Actions for .NET 10, PR tests, and tag releases
|
- Updated GitHub Actions for .NET 10, PR tests, and tag releases
|
||||||
- Switched logging to `BasePlugin.Logger`
|
- Switched logging to `BasePlugin.Logger`
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
# Flashlight Modernization Implementation Plan
|
|
||||||
|
|
||||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
||||||
|
|
||||||
**Goal:** Modernize the flashlight plugin to .NET 10 / CSS 1.0.371 with a parented `light_barn` implementation, config, real unit tests, fixed CI, and updated README.
|
|
||||||
|
|
||||||
**Architecture:** Pure helpers in `FlashlightLogic` + config clamps in `FlashlightConfig`; plugin owns lifecycle and wires Use/command/events; one `CBarnLight` per active player, parented once, toggled via create/destroy.
|
|
||||||
|
|
||||||
**Tech Stack:** .NET 10, CounterStrikeSharp.API 1.0.371, xUnit, GitHub Actions
|
|
||||||
|
|
||||||
## Global Constraints
|
|
||||||
|
|
||||||
- Target framework: `net10.0`
|
|
||||||
- Package: `CounterStrikeSharp.API` `1.0.371`
|
|
||||||
- `[MinimumApiVersion(371)]`
|
|
||||||
- Module version: `0.1.0`
|
|
||||||
- No per-tick entity create/spawn/teleport in steady state
|
|
||||||
- Spec: `docs/superpowers/specs/2026-07-21-flashlight-modernization-design.md`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Task 1: Project targets + pure logic + tests
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `Flashlight/Flashlight.csproj`
|
|
||||||
- Modify: `Flashlight.Tests/Flashlight.Tests.csproj`
|
|
||||||
- Create: `Flashlight/FlashlightConfig.cs`
|
|
||||||
- Create: `Flashlight/FlashlightLogic.cs`
|
|
||||||
- Create: `Flashlight/PlayerFlashlightState.cs`
|
|
||||||
- Modify: `Flashlight.Tests/FlashlightLogicTests.cs`
|
|
||||||
|
|
||||||
- [x] Retarget both projects to `net10.0`; bump CSS to `1.0.371`; bump test packages as needed
|
|
||||||
- [x] Implement `FlashlightConfig` with defaults from the spec and a `Clamp()` method
|
|
||||||
- [x] Implement pure `FlashlightLogic` helpers: try-toggle with cooldown, eye Z offset, origin from base+forward
|
|
||||||
- [x] Replace smoke tests with tests against those helpers
|
|
||||||
- [x] Run `dotnet test` and confirm pass
|
|
||||||
|
|
||||||
### Task 2: Plugin rewrite (parented light_barn)
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `Flashlight/Flashlight.cs`
|
|
||||||
- Delete or gut: `Flashlight/LogHelper.cs` (prefer `Logger`)
|
|
||||||
|
|
||||||
- [x] Implement `BasePlugin, IPluginConfig<FlashlightConfig>`
|
|
||||||
- [x] OnTick: Use-key edge + cooldown only when `AllowUseKey`
|
|
||||||
- [x] Create/parent/enable `light_barn` once on toggle on; remove on toggle off
|
|
||||||
- [x] Cleanup on death/spawn/team/disconnect/unload
|
|
||||||
- [x] Keep `css_fl_toggle`
|
|
||||||
- [x] Run `dotnet build` and confirm success
|
|
||||||
|
|
||||||
### Task 3: CI + README
|
|
||||||
|
|
||||||
**Files:**
|
|
||||||
- Modify: `.github/workflows/build.yml`
|
|
||||||
- Modify: `README.md`
|
|
||||||
|
|
||||||
- [x] CI: .NET 10, restore, test, release build
|
|
||||||
- [x] README: versions, parented light, config table, build/test, changelog 0.1.0
|
|
||||||
- [x] Run `dotnet test` once more
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
# Flashlight Plugin Modernization Design
|
|
||||||
|
|
||||||
**Date:** 2026-07-21
|
|
||||||
**Status:** Approved
|
|
||||||
**Version target:** 0.1.0
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
Modernize the CounterStrikeSharp flashlight plugin to the latest CSS/.NET stack, replace the per-tick spawn/teleport light path with a parented `light_barn` implementation, add `IPluginConfig` for admin tuning, improve testability, and update the README.
|
|
||||||
|
|
||||||
## Background
|
|
||||||
|
|
||||||
The current plugin (`Flashlight` v0.0.7) targets CounterStrikeSharp.API `1.0.363` on .NET 8. While the flashlight is on, `OnTick` creates a `light_omni2` and calls `DispatchSpawn` every server frame. That is the dominant performance problem. Existing tests only exercise inlined dictionary/bool logic and do not cover extractable plugin helpers. CI still installs .NET 7.
|
|
||||||
|
|
||||||
Latest CounterStrikeSharp.API is `1.0.371` and targets .NET 10. CS2Fixes demonstrates the preferred flashlight pattern: spawn `light_barn` once, set a flashlight lightcookie via entity keyvalues, parent to the player pawn attachment, and toggle enablement rather than teleporting every tick.
|
|
||||||
|
|
||||||
## Decisions
|
|
||||||
|
|
||||||
| Topic | Decision |
|
|
||||||
| --- | --- |
|
|
||||||
| Runtime | .NET 10 + CounterStrikeSharp.API 1.0.371 |
|
|
||||||
| Light entity | `light_barn` (`CBarnLight`), parented once |
|
|
||||||
| Position updates | Engine parenting; no per-tick teleport |
|
|
||||||
| Config | `IPluginConfig<FlashlightConfig>` with JSON config |
|
|
||||||
| Logging | Prefer `BasePlugin.Logger` |
|
|
||||||
| README | Fully updated for new runtime, behavior, config, build/test |
|
|
||||||
| Module version | 0.1.0 |
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
FlashlightPlugin (BasePlugin, IPluginConfig)
|
|
||||||
├── FlashlightConfig // JSON-backed settings + clamps
|
|
||||||
├── PlayerFlashlightState // per-player flags + entity handle
|
|
||||||
├── FlashlightService // create / parent / enable / destroy
|
|
||||||
└── FlashlightLogic // pure helpers (toggle, cooldown, offsets)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Runtime flow
|
|
||||||
|
|
||||||
1. `OnConfigParsed` validates/clamps config.
|
|
||||||
2. `Load` registers `OnTick` (Use-key edge + cooldown only), game event handlers, and command `css_fl_toggle`.
|
|
||||||
3. When a player turns the light on:
|
|
||||||
- Create `CBarnLight` via `Utilities.CreateEntityByName<CBarnLight>("light_barn")`.
|
|
||||||
- Apply config (brightness, range, color, temperature, soft/skirt/size, cast shadows, direct light).
|
|
||||||
- Compute initial origin: pawn origin + eye Z offset + forward * `ForwardDistance`.
|
|
||||||
- `Teleport` once using `System.Numerics.Vector3` overloads (avoid legacy `Vector` allocs).
|
|
||||||
- `DispatchSpawn(CEntityKeyValues)` with `lightcookie` = configured path (default flashlight vtex).
|
|
||||||
- `AcceptInput("SetParent", pawn, …)` then `AcceptInput("SetParentAttachmentMaintainOffset", …, AttachmentName)`.
|
|
||||||
- Set `Enabled = true`.
|
|
||||||
4. When turned off: set `Enabled = false` and remove the entity (or disable and keep — prefer remove to avoid orphaned entities across pawn changes).
|
|
||||||
5. Cleanup on death, team change, disconnect, and plugin unload.
|
|
||||||
|
|
||||||
### Why not keep tick teleport?
|
|
||||||
|
|
||||||
Parenting follows view/attachment with far less managed work and no entity churn. Tick work is limited to scanning connected humans for Use-button edges and cooldown expiry.
|
|
||||||
|
|
||||||
## Config surface
|
|
||||||
|
|
||||||
File written/loaded by CSS config system (standard plugin config JSON).
|
|
||||||
|
|
||||||
| Key | Type | Default | Notes |
|
|
||||||
| --- | --- | --- | --- |
|
|
||||||
| `Enabled` | bool | `true` | Master switch |
|
|
||||||
| `AllowUseKey` | bool | `true` | Toggle via Use (`E`) |
|
|
||||||
| `ToggleCooldownSeconds` | float | `0.25` | Clamp ≥ 0 |
|
|
||||||
| `Brightness` | float | `1.0` | |
|
|
||||||
| `Range` | float | `2048` | Match CS2Fixes-style defaults |
|
|
||||||
| `ColorR` / `ColorG` / `ColorB` | byte | `255` | White |
|
|
||||||
| `ColorTemperature` | float | `6500` | |
|
|
||||||
| `CastShadows` | bool | `true` | Maps to `CastShadows` int |
|
|
||||||
| `SoftX` / `SoftY` | float | `1.0` | |
|
|
||||||
| `Skirt` | float | `0.5` | |
|
|
||||||
| `SkirtNear` | float | `1.0` | |
|
|
||||||
| `SizeX` / `SizeY` / `SizeZ` | float | `45` / `45` / `0.03` | `SizeParams` |
|
|
||||||
| `ForwardDistance` | float | `54` | Avoid AWP blocking beam |
|
|
||||||
| `StandEyeOffsetZ` | float | `64` | |
|
|
||||||
| `CrouchEyeOffsetZ` | float | `46` | Used at spawn time only |
|
|
||||||
| `AttachmentName` | string | `axis_of_intent` | Parent attachment |
|
|
||||||
| `LightCookie` | string | `materials/effects/lightcookies/flashlight.vtex` | |
|
|
||||||
|
|
||||||
Invalid values are clamped or rejected in `OnConfigParsed` with log warnings; plugin remains loadable when possible.
|
|
||||||
|
|
||||||
## Player state
|
|
||||||
|
|
||||||
Replace multiple `Dictionary<CCSPlayerController, …>` maps with one structure keyed by player slot (or controller), holding:
|
|
||||||
|
|
||||||
- `IsOn`
|
|
||||||
- `CanToggle`
|
|
||||||
- `Light` (`CBarnLight?`)
|
|
||||||
|
|
||||||
Crouch tracking for continuous Z updates is unnecessary once the light is parented; crouch offset is only applied at creation time. Optional: read duck state at spawn for initial Z only.
|
|
||||||
|
|
||||||
## Event / command behavior (unchanged UX)
|
|
||||||
|
|
||||||
- Use key toggles when `AllowUseKey` is true and cooldown allows.
|
|
||||||
- `css_fl_toggle` remains client-only command alternative.
|
|
||||||
- Flashlight turns off on death and spawn; entity cleaned on team change and disconnect.
|
|
||||||
- Bots ignored.
|
|
||||||
|
|
||||||
## Testing strategy
|
|
||||||
|
|
||||||
Full CSS entity lifecycle cannot be unit-tested without a game server. Extract and test pure logic:
|
|
||||||
|
|
||||||
1. Toggle state transitions and cooldown gating.
|
|
||||||
2. Config clamp helpers (range, cooldown, color channels).
|
|
||||||
3. Initial position offset calculation (stand/crouch Z + forward distance given basis vectors).
|
|
||||||
4. Enable/disable policy: when entity should be created vs destroyed.
|
|
||||||
|
|
||||||
Use xUnit on .NET 10. Keep Moq only if needed; prefer plain helpers over mocking CSS types.
|
|
||||||
|
|
||||||
## CI / packaging
|
|
||||||
|
|
||||||
- GitHub Actions: .NET 10 SDK, `dotnet restore`, `dotnet test`, release build on tags.
|
|
||||||
- Remove stale .NET 7 setup.
|
|
||||||
- Publish zip layout unchanged: `plugins/Flashlight/`.
|
|
||||||
|
|
||||||
## README updates
|
|
||||||
|
|
||||||
- Prerequisites: .NET 10, CSS 1.0.371+.
|
|
||||||
- Behavior: parented `light_barn`, Use + command.
|
|
||||||
- Config table with defaults.
|
|
||||||
- Build / test instructions.
|
|
||||||
- Changelog entry for 0.1.0 (API bump, performance rewrite, config, tests, CI).
|
|
||||||
|
|
||||||
## Out of scope
|
|
||||||
|
|
||||||
- Particle flashlight mode (CS2Fixes mode 2).
|
|
||||||
- Admin permissions / VIP-only flashlight.
|
|
||||||
- Client-side HUD indicators.
|
|
||||||
- Migrating to CounterStrikeSharp 2.0 alpha.
|
|
||||||
|
|
||||||
## Risks / mitigations
|
|
||||||
|
|
||||||
| Risk | Mitigation |
|
|
||||||
| --- | --- |
|
|
||||||
| Attachment name missing on some models | Configurable `AttachmentName`; fall back to parent-only if attachment input fails |
|
|
||||||
| `CEntityKeyValues` lightcookie path differs | Use CS2Fixes-proven path; document override |
|
|
||||||
| .NET 10 server prerequisite | Document clearly; MinimumApiVersion 371 |
|
|
||||||
| Parenting breaks on pawn swap | Recreate light on spawn; destroy on death/team |
|
|
||||||
|
|
||||||
## Success criteria
|
|
||||||
|
|
||||||
- Builds against CounterStrikeSharp.API 1.0.371 on net10.0.
|
|
||||||
- No entity create/spawn/teleport in the steady-state OnTick path.
|
|
||||||
- Config file generated and honored.
|
|
||||||
- Unit tests cover pure helpers and pass in CI.
|
|
||||||
- README matches shipped behavior and versions.
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue