hlmod.hu
https://hlmod.hu/

CounterStrikeSharp API Bemutató: Egy Plugin a Gyakorlatban (Részletes Útmutató)
https://hlmod.hu/viewtopic.php?f=13&t=31634
Oldal: 1 / 1

Szerző:  *tOrNaDo [2025.08.04. 18:49 ]
Hozzászólás témája:  CounterStrikeSharp API Bemutató: Egy Plugin a Gyakorlatban (Részletes Útmutató)

Sziasztok!
Sokan érdeklődtök a Counter-Strike 2 pluginfejlesztés iránt, és ahogy azt már többen tudjátok, a közösség által favorizált út jelenleg a CounterStrikeSharp keretrendszer és a C# programozási nyelv.
Hogy segítsem a kezdőket (és talán a haladóbbakat is), készítettem egy "bemutató" plugint. A célja nem az, hogy egy komoly, éles szerveren használt funkciót lásson el, hanem hogy egy helyen mutassa be a lehető legtöbb alapvető API funkciót, amire egy fejlesztőnek szüksége lehet.
Minden egyes kódsort és logikai egységet részletesen, kommenteztem, hogy ne csak a "mit", hanem a "miért" is érthető legyen. Ez a poszt egy gyakorlati útmutató, ami segít megérteni a pluginok anatómiáját.
Mit tud a plugin?
    * Plugin Alapok: Megmutatja a helyes struktúrát, a metaadatok (név, szerző) megadását.
    * Eseménykezelés (Event Handling): Reagál a játék leggyakoribb eseményeire:
    * player_connect: Üdvözli a csatlakozó játékosokat.
    * round_start: Elvégez egy feladatot minden kör elején (nulláz egy számlálót).
    * player_death: Figyeli a haláleseteket, adatokat nyer ki (ki, kit, mivel), és ez alapján logikát futtat.
    * Állapotkezelés (State Management): Nyilvántartja, hogy egy játékos hány ölést szerzett az adott körben.
    * Parancs Létrehozása: Regisztrál egy /bemutato chat parancsot.
    * Időzítők (Timers): Késleltetett akciót hajt végre (az üdvözlő üzenetet pár másodperccel a csatlakozás után küldi).
    * Menük (Menus): A /bemutato parancsra egy interaktív menüt nyit meg, amivel a játékos:
    * Információt kérhet le magáról (élet, páncél).
    * Tárgyakat adhat magának (egy HE gránátot).
    * Lekérdezheti a körben szerzett öléseinek számát.
    * Szerverinformációt olvashat ki (a szerver nevét).
Remélem, hogy ez a gyakorlati példa sokaknak segít elindulni a CS2 pluginfejlesztés izgalmas útján!
Jó tanulást és jó kódolást mindenkinek!
A kód (ApiBemutatoPlugin.cs)
  1. /*
  2.  * =================================================================================================
  3.  * Using Direktívák (Using Directives)
  4.  * =================================================================================================
  5.  * Ezekkel a sorokkal "importáljuk" a CounterStrikeSharp API különböző részeit.
  6.  * Mindegyik egy-egy funkciócsoportért felel, pl. parancsokért, menükért, eseményekért stb.
  7.  * Így tud a kódunk hivatkozni a beépített funkciókra, mint pl. a PrintToChat vagy a AddCommand.
  8.  */
  9. using CounterStrikeSharp.API;
  10. using CounterStrikeSharp.API.Core;
  11. using CounterStrikeSharp.API.Core.Attributes.Registration;
  12. using CounterStrikeSharp.API.Modules.Commands;
  13. using CounterStrikeSharp.API.Modules.Menus;
  14. using CounterStrikeSharp.API.Modules.Timers;
  15. using CounterStrikeSharp.API.Modules.Utils;
  16.  
  17. // A névtér (namespace) segít a kódunkat logikailag elkülöníteni más pluginektől.
  18. // Jó gyakorlat, ha minden plugin egyedi névtérben van.
  19. namespace ApiBemutatoPlugin;
  20.  
  21. // A fő plugin osztályunk, ami a BasePlugin-ból öröklődik. Minden pluginnek így kell kezdődnie.
  22. public class ApiBemutatoPlugin : BasePlugin
  23. {
  24.     /*
  25.      * =================================================================================================
  26.      * Plugin Metaadatok (Plugin Metadata)
  27.      * =================================================================================================
  28.      * Ezek az adatok jelennek meg a szerver konzoljában, amikor a plugin betöltődik.
  29.      * Segítenek azonosítani a plugint és annak verzióját.
  30.      */
  31.     public override string ModuleName => "CSS API Bemutató Plugin";
  32.     public override string ModuleAuthor => "*tOrNaDo";
  33.     public override string ModuleVersion => "1.0.0";
  34.    
  35.     /*
  36.      * =================================================================================================
  37.      * Állapotkezelés (State Management)
  38.      * =================================================================================================
  39.      * Itt egy ún. "Dictionary"-t (szótárat) hozunk létre.
  40.      * Ebben fogjuk tárolni, hogy egy adott játékos (akit az UserID-ja azonosít)
  41.      * hány ölést szerzett az aktuális körben.
  42.      * Ez egy alapvető példa az állapotkezelésre, amikor adatokat kell tárolnunk a játékosokról.
  43.      */
  44.     private readonly Dictionary<int, int> _playerRoundKills = new();
  45.  
  46.     /*
  47.      * =================================================================================================
  48.      * Eseménykezelők (Event Handlers)
  49.      * =================================================================================================
  50.      * A [GameEventHandler] attribútummal "feliratkozunk" a játék eseményeire.
  51.      * Amikor a játékban történik valami (pl. egy játékos meghal), a CounterStrikeSharp
  52.      * meghívja az eseményhez tartozó függvényünket.
  53.      */
  54.    
  55.     /// <summary>
  56.     /// Ez a függvény minden kör elején lefut (a freeze time kezdetekor).
  57.     /// </summary>
  58.     [GameEventHandler]
  59.     public HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
  60.     {
  61.         // Kiürítjük a szótárunkat, hogy az új kör tiszta lappal induljon mindenki számára.
  62.         // Ha ezt nem tennénk, az ölések száma körökön át összeadódna.
  63.         _playerRoundKills.Clear();
  64.        
  65.         // Kiírunk egy üzenetet a szerver chatjébe mindenki számára.
  66.         // A ChatColors segítségével színesíthetjük a szöveget.
  67.         Server.PrintToChatAll(\$" {ChatColors.Green}Új kör kezdődött! Az ölési statisztikák nullázva.");
  68.        
  69.         // A HookResult.Continue jelzi a CounterStrikeSharp-nak, hogy a többi plugin is feldolgozhatja ezt az eseményt.
  70.         return HookResult.Continue;
  71.     }
  72.  
  73.     /// <summary>
  74.     /// Akkor fut le, amikor egy játékos sikeresen csatlakozott a szerverre.
  75.     /// </summary>
  76.     [GameEventHandler]
  77.     public HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
  78.     {
  79.         // Lekérjük a csatlakozott játékos objektumát az eseményből.
  80.         // A try-catch blokk biztonsági okokból van itt, ha esetleg a játékos érvénytelen lenne.
  81.         try
  82.         {
  83.             var player = @event.Userid;
  84.  
  85.             // Ellenőrizzük, hogy a játékos valid-e és nem egy bot.
  86.             if (player == null || !player.IsValid || player.IsBot)
  87.             {
  88.                 return HookResult.Continue;
  89.             }
  90.            
  91.             // Létrehozunk egy időzítőt (Timer), ami 3 másodperc múlva futtatja le a benne lévő kódot.
  92.             // Erre azért van szükség, mert a csatlakozáskor sok üzenet jelenik meg,
  93.             // és így a miénk nem veszik el a többi között.
  94.             AddTimer(3.0f, () =>
  95.             {
  96.                 // Mielőtt üzenetet küldenénk, újra ellenőrizzük, hogy a játékos még mindig valid-e.
  97.                 // Lehet, hogy 3 mp alatt kilépett.
  98.                 if (player.IsValid)
  99.                 {
  100.                     // Privát üzenetet küldünk a csatlakozott játékosnak.
  101.                     player.PrintToChat(\$" {ChatColors.Gold}Üdv a szerveren, {ChatColors.Lime}{player.PlayerName}{ChatColors.Gold}!");
  102.                     player.PrintToChat(\$" {ChatColors.LightBlue}Ez a szerver egy API bemutató plugint futtat.");
  103.                     player.PrintToChat(\$" {ChatColors.LightBlue}Írd be a chatbe: {ChatColors.Olive}/bemutato");
  104.                 }
  105.             }, TimerFlags.STOP_ON_MAPCHANGE);
  106.         }
  107.         catch (Exception e)
  108.         {
  109.             // Hiba esetén kiírjuk a konzolra a hibaüzenetet.
  110.             Console.WriteLine(\$"[ApiBemutato] Hiba OnPlayerConnectFull eseménynél: {e.Message}");
  111.         }
  112.        
  113.         return HookResult.Continue;
  114.     }
  115.  
  116.     /// <summary>
  117.     /// Ez a legkomplexebb eseménykezelőnk. Akkor fut le, amikor egy játékos meghal.
  118.     /// </summary>
  119.     [GameEventHandler]
  120.     public HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
  121.     {
  122.         // Lekérjük az áldozatot (victim) és a támadót (attacker) az esemény adataiból.
  123.         var victim = @event.Userid;
  124.         var attacker = @event.Attacker;
  125.        
  126.         // Ellenőrizzük, hogy a támadó és az áldozat is érvényes játékos-e.
  127.         if (attacker == null || !attacker.IsValid || victim == null || !victim.IsValid)
  128.         {
  129.             return HookResult.Continue;
  130.         }
  131.  
  132.         // Ha a támadó és az áldozat ugyanaz a személy (öngyilkosság), nem csinálunk semmit.
  133.         if (attacker == victim)
  134.         {
  135.             return HookResult.Continue;
  136.         }
  137.  
  138.         // Ha a támadó egy bot, szintén nem foglalkozunk vele.
  139.         if (attacker.IsBot)
  140.         {
  141.             return HookResult.Continue;
  142.         }
  143.        
  144.         // Növeljük a támadó ölési számlálóját.
  145.         // Először megnézzük, szerepel-e már a szótárban. Ha nem, hozzáadjuk 0 értékkel.
  146.         if (!_playerRoundKills.ContainsKey(attacker.UserId ?? 0))
  147.         {
  148.             _playerRoundKills.Add(attacker.UserId ?? 0, 0);
  149.         }
  150.        
  151.         // Növeljük az értékét eggyel.
  152.         _playerRoundKills[attacker.UserId ?? 0]++;
  153.        
  154.         // Kiolvassuk az aktuális ölési számot.
  155.         var kills = _playerRoundKills[attacker.UserId ?? 0];
  156.        
  157.         // Kiírunk egy informatív üzenetet a chatbe mindenki számára.
  158.         Server.PrintToChatAll(
  159.             \$" {ChatColors.Lime}{attacker.PlayerName} {ChatColors.White}megölte {ChatColors.Red}{victim.PlayerName} {ChatColors.White}játékost" +
  160.             \$" egy {ChatColors.Gold}{@event.Weapon}{ChatColors.White} fegyverrel." +
  161.             \$" ({ChatColors.Lime}{kills}{ChatColors.White}. ölés a körben)"
  162.         );
  163.        
  164.         return HookResult.Continue;
  165.     }
  166.    
  167.     /*
  168.      * =================================================================================================
  169.      * Parancsok és Menük (Commands and Menus)
  170.      * =================================================================================================
  171.      * A [ConsoleCommand] attribútummal regisztrálunk egy parancsot, amit a játékosok a chatbe írhatnak.
  172.      * A css_ előtag automatikusan hozzáadódik, a chat parancsok általában / vagy ! jellel kezdődnek.
  173.      */
  174.    
  175.     /// <summary>
  176.     /// A /bemutato parancsot lekezelő függvény.
  177.     /// </summary>
  178.     [ConsoleCommand("css_bemutato", "Megnyitja az API bemutató menüt.")]
  179.     [CommandHelper(whoCanExecute: CommandUsage.CLIENT_ONLY)] // Biztosítja, hogy csak játékosok használhassák, a szerverkonzol ne.
  180.     public void OnShowMenuCommand(CCSPlayerController? player, CommandInfo command)
  181.     {
  182.         // Ellenőrizzük, hogy a parancsot kiadó játékos érvényes-e.
  183.         if (player == null || !player.IsValid)
  184.         {
  185.             return;
  186.         }
  187.        
  188.         // Létrehozunk egy új menüt, aminek címet adunk.
  189.         var menu = new ChatMenu(\$"{ChatColors.Gold}API Bemutató Menü");
  190.        
  191.         // Hozzáadunk egy menüpontot. A második paraméter egy ún. lambda függvény,
  192.         // ami akkor fut le, ha a játékos ezt a pontot választja.
  193.         menu.AddMenuOption("Élet és Páncél Lekérdezése", (p, option) =>
  194.         {
  195.             // Kiírjuk a játékosnak az aktuális életpontját és páncélját.
  196.             p.PrintToChat(\$" {ChatColors.Green}Életerő: {p.Pawn.Value.Health}");
  197.             p.PrintToChat(\$" {ChatColors.Green}Páncél: {p.Pawn.Value.ArmorValue}");
  198.         });
  199.        
  200.         // Újabb menüpont, ami egy HE gránátot ad a játékosnak.
  201.         menu.AddMenuOption("Adj egy HE gránátot!", (p, option) =>
  202.         {
  203.             // A GiveNamedItem függvénnyel adhatunk tárgyakat a játékosnak.
  204.             p.GiveNamedItem("weapon_hegrenade");
  205.             p.PrintToChat(\$" {ChatColors.Green}Megkaptad a HE gránátot!");
  206.         });
  207.        
  208.         // Menüpont a körben szerzett ölések lekérdezésére.
  209.         menu.AddMenuOption("Kör statisztikám", (p, option) =>
  210.         {
  211.             // Kiolvassuk az adatot a szótárunkból. Ha a játékos még nem ölt, 0 lesz az érték.
  212.             _playerRoundKills.TryGetValue(p.UserId ?? 0, out int kills);
  213.             p.PrintToChat(\$" {ChatColors.LightBlue}Ebben a körben eddig {ChatColors.Lime}{kills}{ChatColors.LightBlue} ölésed van.");
  214.         });
  215.  
  216.         // Menüpont szerverinformáció lekérdezésére.
  217.         menu.AddMenuOption("Szerver neve", (p, option) =>
  218.         {
  219.             // A ConVar.Find segítségével kiolvashatjuk a szerver változóit (ConVar).
  220.             // A 'sv_hostname' a szerver nevét tárolja.
  221.             var hostname = ConVar.Find("sv_hostname")?.StringValue ?? "Ismeretlen";
  222.             p.PrintToChat(\$" {ChatColors.Grey}A szerver neve: {ChatColors.Olive}{hostname}");
  223.         });
  224.  
  225.         // Végül megnyitjuk a létrehozott menüt a parancsot használó játékosnak.
  226.         MenuManager.OpenChatMenu(player, menu);
  227.     }
  228. }

Oldal: 1 / 1 Minden időpont UTC+02:00 időzóna szerinti
Powered by phpBB® Forum Software © phpBB Limited
https://www.phpbb.com/