Skip to content

v2.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 16 Sep 19:18

⚠ BREAKING CHANGES

  • the k4-worldtext:sharedapi capability now serves IK4WorldTextProvider instead of IK4WorldTextSharedAPI. A consumer built against 1.x fails at Capability.Get() with KeyNotFoundException once 2.0 is installed. Change the field to PluginCapability<IK4WorldTextProvider> and call ForPlugin(this) on the result.
  • config persistence is gone. The saveConfig, removeFromConfig, and modifyConfig parameters were removed, RemoveAllTemporary() was replaced by RemoveAll(), and nothing is written to or read from disk. Re-create texts from your own map-start handling.

Features

  • api: scope the shared API per consumer plugin. IK4WorldTextProvider.ForPlugin(IPlugin owner) returns a scope keyed by ModuleName; every text belongs to the plugin that created it, an id issued to another plugin is treated as unknown, foreign-id access is logged with the real owner's name, and RemoveAll() removes only the caller's texts. The same plugin gets the same scope after a hot reload, so it can clear texts left by its previous instance (0402eac)
  • api: remove the config persistence parameters from the shared API and rewrite the README around the shared-API-only model (aa197d3)
  • api: document every public member of the shared API and ship K4-WorldText-SharedAPI.xml next to the DLL, so the argument, threading, and clamping rules show up in IntelliSense (60cf26f)

Bug Fixes

  • threading: marshal API entity work onto the game thread and make text tracking concurrent, so calls from a Task.Run body, an HttpClient continuation, or a database callback no longer touch entity memory off-thread (e51b65c)
  • threading: run the entity work inline when the caller is already on the game thread, so a text exists in the world by the time AddWorldText returns, and capture the game thread id synchronously in Load instead of racing the first world update (001d67c) (e7cf60b)
  • api: validate arguments synchronously on the calling thread and throw ArgumentException before any work is queued, instead of failing later on the game thread with no way to report it (daaa8ad)
  • api: validate CCSPlayerController and its pawn at the boundary and again inside the deferred spawn, so a player who disconnects between the call and the world update produces a logged warning instead of a crash (f12a002)
  • api: copy the caller's Vector/QAngle at the boundary so a respawn never reads entity-backed memory, making pawn.AbsOrigin safe to pass (8757296)
  • api: contain entity-work exceptions and throw KeyNotFoundException for unknown text ids. A failed spawn rolls back, logs, keeps the id tracked, and retries on the next round start, rather than surfacing at the caller (d180290)
  • api: return null from GetWorldTextLineEntities for unknown ids and skip entities that are no longer valid, instead of handing back dangling pointers (5494db2)
  • api: log rejected shared-API calls before throwing, so a misbehaving consumer is identifiable from the server log (a690c50)
  • entities: clamp point_worldtext MessageText to its 512-byte budget, truncating on a character boundary (8a1a998)
  • entities: clamp the numeric TextLine styling fields and truncate FontName to the entity's 64-byte field before writing to native memory; NaN becomes the in-range value closest to zero (ebc651c)
  • entities: dispose WorldText children on removal and guard the mutators after dispose, and untrack disposed texts so a disposed MultilineWorldText is never re-used (af6014f) (f5abea6)
  • geometry: normalize yaw to [-180,180) in EntityFaceToDirection (ec2c2be)
  • geometry: derive the floor line stacking direction from absRotation instead of the previous entity, so multi-line floor texts stack correctly (07a6f3f)
  • perf: update world text entities in place instead of respawning them when the line count, font sizes, and background layout are unchanged (38f215a)
  • perf: teleport world text entities in place, respawning only when one of them is no longer valid (55afc76)
  • perf: skip the round-start reapply for texts whose entities survived the round transition, and cache entity validity in that check (2e5b79d) (2a86740)

Code Refactoring

  • mark the geometry and spawn helpers internal so they are no longer part of the public surface (5392a19)

API Changes (1.3.0 -> 2.0.0)

Capability

  • k4-worldtext:sharedapi (name unchanged) now serves IK4WorldTextProvider instead of IK4WorldTextSharedAPI

New interface

  • IK4WorldTextProvider
    • IK4WorldTextSharedAPI ForPlugin(IPlugin owner) - the scope for one consumer plugin, keyed by ModuleName. Throws ArgumentNullException for a null owner and ArgumentException for an empty ModuleName. Safe to call from any thread

Removed parameters

  • saveConfig from both AddWorldText overloads and both AddWorldTextAtPlayer overloads
  • removeFromConfig from RemoveWorldText
  • modifyConfig from TeleportWorldText

Renamed methods

  • RemoveAllTemporary() -> RemoveAll(), now scoped to the calling plugin

Changed behaviour

  • Ids are per scope. UpdateWorldText, RemoveWorldText, and TeleportWorldText throw KeyNotFoundException for an id that is not tracked by the calling scope, including every id issued before the last map change; GetWorldTextLineEntities returns null instead
  • Bad arguments throw ArgumentException synchronously on the calling thread before any entity work is queued
  • GetWorldTextLineEntities is game thread only and throws InvalidOperationException elsewhere, because raw entity pointers cannot be marshalled
  • textLines is copied at the call, so the caller may reuse or clear its list afterwards
  • TextLine numeric fields are clamped, never rejected: FontSize 1-500, Scale and BackgroundScale 0.01-10, BackgroundWorldToUV 0.001-10, BackgroundBorderWidth/BackgroundBorderHeight 0-1000, the depth offsets ±100, BackgroundMaxCharsPerLine 1-256, BackgroundPadChars 0-256, BackgroundWidthInflation 0.1-10. FontName is truncated to 64 UTF-8 bytes and Text to 512
  • Nothing is persisted to disk. Every text lives until its owner removes it or the map ends. Unloading or reloading the plugin destroys every text and resets the id counter, so ids restart from 1

Miscellaneous

  • deps: remove the unused Newtonsoft.Json package reference (c824519)

Full Changelog: v1.3.0...v2.0.0