Server Help

ASSS Questions - WatchDamage module

Icebird - Sun Sep 21, 2008 10:09 am
Post subject: WatchDamage module
Hi.

I need some assistance with the watchdamage module. I do load it in modules.conf and it is attached via arena.conf. Here are the important bits of my own module code:

Code: Show/Hide

#include <string.h>

#include "asss.h"
#include "fake.h"
#include "watchdamage.h"
#include "defs.h"

...

// Interfaces
local Imodman *mm;
local Iplayerdata *pd;
local Imainloop *ml;
local Iconfig *conf;
local Inet *net;
local Iarenaman *aman;
local Ifake *fake;
local Igame *game;
local Ichat *chat;
local Iwatchdamage *dmg;

// Data
...

typedef struct SSSC_Shields_PlayerData
{
   int health;
   int shields;
} SSSC_Shields_PlayerData;

...

...

// This function will be called when a player changes ship
local void ShipChangeCallback(Player *p, int newship, int newfreq)
{
   const char *shipname = ShipName((i8)newship);
   SSSC_Shields_PlayerData *dataPlayer = PPDATA(p, playerKey);
   
   chat->SendMessage(p, "SSSC_shields - Ship change detected");

   // read the health and shield hit points from the config and assign them
   dataPlayer->health = conf->GetInt(p->arena->cfg, "Health", shipname, 0);
   dataPlayer->shields = conf->GetInt(p->arena->cfg, "Shields", shipname, 0);
   
   // manage watchdamage
   dmg->ModuleWatch(p, 0);
   
   if (0 < dataPlayer->shields)
   {
      chat->SendMessage(p, "SSSC_shields - You have shields");
      dmg->ModuleWatch(p, 1);
      dmg->AddWatch(fake->CreateFakePlayer("Damage", p->arena, SHIP_WARBIRD, 9999), p);
   }
}

...

...

// This function is called every time a player receives damage
local void PlayerDamageCallback(Arena *arena, Player *p, struct S2CWatchDamage *damage, int count)
{
   SSSC_Shields_PlayerData *dataPlayer = PPDATA(p, playerKey);
   const char *shipname;
   int maxhealth;
   int   maxshields;
   int dmg_val;
   
   shipname = ShipName(p->pkt.ship);
   maxhealth = conf->GetInt(arena->cfg, "Health", shipname, 0);
   maxshields = conf->GetInt(arena->cfg, "Shields", shipname, 0);
   dmg_val = (int)damage->damage;
   chat->SendMessage(p, "SSSC_shields - You took %i damage", dmg_val);
   if (0 < maxshields)
   // the ship type has shields
   {
      if (dataPlayer->shields > 0)
      // the player still has some shields
      {
         if (dataPlayer->shields < dmg_val)
         // some of the damage gets through the shields
         {
            dataPlayer->health -= dmg_val - dataPlayer->shields;
            dataPlayer->shields = 0;
         }
         else
         // the damage is completely absorbed
         {
            dataPlayer->shields -= dmg_val;
         }
         
      }
      else
      // no shields left
      {
         dataPlayer->health -= dmg_val;
      }
      
      // kill the player if no health is left
      if (dataPlayer->health < 0) kill(p, "killer", 0, 0);
      
      // update the interface
      UpdateInterface(p);
   }
}

// The entry point:
EXPORT int MM_sssc_shields(int action, Imodman *mm_, Arena *arena)
{
   int rv = MM_FAIL; // return value

   if (action == MM_LOAD)
   {
      mm = mm_;

      pd = mm->GetInterface(I_PLAYERDATA, ALLARENAS);
      ml = mm->GetInterface(I_MAINLOOP, ALLARENAS);
      conf = mm->GetInterface(I_CONFIG, ALLARENAS);
      net = mm->GetInterface(I_NET, ALLARENAS);
      aman = mm->GetInterface(I_ARENAMAN, ALLARENAS);
      fake = mm->GetInterface(I_FAKE, ALLARENAS);
      game = mm->GetInterface(I_GAME, ALLARENAS);
      chat = mm->GetInterface(I_CHAT, ALLARENAS);
      dmg = mm->GetInterface(I_WATCHDAMAGE, ALLARENAS);

      if (!pd || !ml || !conf || !net || !aman || !fake || !game || !chat || !dmg)
      {
         // release interfaces if loading failed
         mm->ReleaseInterface(dmg);
         mm->ReleaseInterface(chat);
         mm->ReleaseInterface(game);
         mm->ReleaseInterface(fake);
         mm->ReleaseInterface(aman);
         mm->ReleaseInterface(net);
         mm->ReleaseInterface(conf);
         mm->ReleaseInterface(ml);
         mm->ReleaseInterface(pd);
         rv = MM_FAIL;
      }
      else
      {
         // allocate data
         playerKey = pd->AllocatePlayerData(sizeof(SSSC_Shields_PlayerData));
         arenaKey = aman->AllocateArenaData(sizeof(ArenaData));

         if (playerKey == -1 || arenaKey == -1) // check if we ran out of memory
         {
            // release interfaces if loading failed
            mm->ReleaseInterface(dmg);
            mm->ReleaseInterface(chat);
            mm->ReleaseInterface(game);
            mm->ReleaseInterface(fake);
            mm->ReleaseInterface(aman);
            mm->ReleaseInterface(net);
            mm->ReleaseInterface(conf);
            mm->ReleaseInterface(ml);
            mm->ReleaseInterface(pd);

            if (playerKey != -1) // free data if it was allocated
               pd->FreePlayerData(playerKey);
               
            if (arenaKey != -1) // free data if it was allocated
               aman->FreeArenaData(arenaKey);

            rv = MM_FAIL;
         }
         else
         {
            // callbacks
            mm->RegCallback(CB_PLAYERACTION, playerActionCallback, ALLARENAS);
            mm->RegCallback(CB_ARENAACTION, arenaActionCallback, ALLARENAS);
            mm->RegCallback(CB_SHIPCHANGE, ShipChangeCallback, ALLARENAS);
            mm->RegCallback(CB_FREQCHANGE, FreqChangeCallback, ALLARENAS);
            mm->RegCallback(CB_PLAYERDAMAGE, PlayerDamageCallback, ALLARENAS);

            // timers
            ml->SetTimer(EverySecond, 100, 100, NULL, NULL); // times are in centiseconds

            // commands

            rv = MM_OK;
         }
      }
   }
   else if (action == MM_UNLOAD)
   {   
      // clear timers
      ml->ClearTimer(EverySecond, NULL);
      ml->ClearTimer(unload_timer, NULL);

      // unregister callbacks
      mm->UnregCallback(CB_ARENAACTION, arenaActionCallback, ALLARENAS);
      mm->UnregCallback(CB_PLAYERACTION, playerActionCallback, ALLARENAS);
      mm->UnregCallback(CB_SHIPCHANGE, ShipChangeCallback, ALLARENAS);
      mm->UnregCallback(CB_FREQCHANGE, FreqChangeCallback, ALLARENAS);
      mm->UnregCallback(CB_PLAYERDAMAGE, PlayerDamageCallback, ALLARENAS);

      // free allocated data
      pd->FreePlayerData(playerKey);
      aman->FreeArenaData(arenaKey);

      // release interfaces
      mm->ReleaseInterface(dmg);
      mm->ReleaseInterface(chat);
      mm->ReleaseInterface(game);
      mm->ReleaseInterface(fake);
      mm->ReleaseInterface(aman);
      mm->ReleaseInterface(net);
      mm->ReleaseInterface(conf);
      mm->ReleaseInterface(ml);
      mm->ReleaseInterface(pd);
      rv = MM_OK;
   }

   return rv;
}


I do register the CB_PLAYERDAMAGE and when a player enters a ship that has shields I setup watches with ModuleWatch and AddWatch. I think I need the AddWatch too because ModuleWatch does not call ToggleWatch, correct?

I see the "Ship change detected" and "You have shields" in ShipChangeCallback but I never see the "SSSC_shields - You took %i damage" message in PlayerDamageCallback when I shoot bombs at nearby walls to damage myself.
Dr Brain - Sun Sep 21, 2008 10:24 am
Post subject:
Do /?watchdamage and then try.

Be warned that watching player damage will probably triple your bandwidth usage. If you've got low population, it shouldn't pose an issue.
tcsoccerman - Sun Sep 21, 2008 11:20 am
Post subject:
Oh you're the one who wanted this module. I started it but never really tested or finished.

I'll attach my work incase it's useful.
Icebird - Sun Sep 21, 2008 5:43 pm
Post subject:
Thank you for your answers. I found the problem after taking a closer look at watchdamage.c.

I always called ModuleWatch and after that AddWatch.

AddWatch has this check at the end:

Code: Show/Hide
if (WATCHCOUNT(wd) == 1)
      ToggleWatch(target, 1);


and WATCHCOUNT is:

Code: Show/Hide
#define WATCHCOUNT(wd) \
   ( LLCount(&wd->watches) + wd->modwatch )


So if i first call ModuleWatch this count is 2 when the check is reached because AddWatch appends a new element to wd->watches just before which makes its LLCount 1 and wd->modwatch is also 1. I wondered if it shouldn't be "if (WATCHCOUNT(wd) >= 1)" but then the package in ToggleWatch would be sent every time a new watch is created for that player which is not necessary.
I think the fix for this would be to append the same check at the end of ModuleWatch so it would look this way:

Code: Show/Hide
local void ModuleWatch(Player *p, int on)
{
   watchdata *wd = PPDATA(p, wdkey);
   if (on)
      wd->modwatch++;
   else
      wd->modwatch -= wd->modwatch > 0 ? 1 : 0;
   
   if (WATCHCOUNT(wd) == 1)
      ToggleWatch(p, 1);
   else if (WATCHCOUNT(wd) == 0)
      ToggleWatch(p, 0);
}

All times are -5 GMT
View topic
Powered by phpBB 2.0 .0.11 © 2001 phpBB Group