Author Topic: Error: SpriteCache::removeOldest - does anyone have any advice?  (Read 2272 times)

I get the following message as my game crashes about once every playthrough
Quote
An internal error has occured. Please note down the following information.
If the problem persists, please post the details on the AGS technical forum.
(ACI Version 3.21.1115)

Error: SpriteCache::removeOldest: Attempted to remove sprite 1200 that does not exist

This problem has been raised before on the forums but I haven't seen a solution.
Any suggestions?

P.S. I am using dynamic sprites. What I've noticed is that the crashes tend to happen shortly after a Changeview, but not always the same one, and when I resume the game from the last save it doesn't crash in the same place.
« Last Edit: 10 Nov 2012, 01:26 by Hernald »

Well, the error message suggests that there was an attempt to delete same sprite twice.
On other hand, it happens deep in the engine, which does not look good (maybe AGS cannot detect problem before it screws things up badly).
This may be because of mistake in your script, or in AGS, or both.

I think best thing would be to post your script here so others could check it. Specifically parts:
- where you create dynamic sprites
- where you delete dynamic sprites
- where you call ChangeView (since you think this is related to crash).

Thanks for getting back to me Crimson. I will post some selected highlights of the code here over the next few days.

...Okay. Having taken another look at my code I realize there should probably be a lot more Deletes in it. I will get onto that;
but as the problem is probably the Deletes I have put in rather than the Deletes I have left out I will focus on them.

There are a couple of instances in my Speak code where vOldSprite is declared globally:
Code: Adventure Game Studio
  1. // Speak: Say with a background panel.
  2.  
  3. void Speak(this Character*, String message) {
  4.   int wid;
  5.   int hei;
  6.   int lines;
  7.   int maxwid = 428;
  8.   int lesswid = 290;//257
  9.  
  10.   int newwid;
  11.  
  12.   int leftside = 160;
  13.   int rightside = 480;
  14.  
  15.   int savewid;
  16.  
  17.   int char_width;
  18.   int char_height;
  19.   ViewFrame *char_frame;
  20.   DynamicSprite *char_sprite;
  21.  
  22.   bool HotsVis;
  23.  
  24.   // Get Character dimensions.
  25.  
  26.   char_frame = Game.GetViewFrame(this.View, this.Loop, this.Frame);
  27.   char_sprite= DynamicSprite.CreateFromExistingSprite(char_frame.Graphic);
  28.   char_width=char_sprite.Width;
  29.   char_height=char_sprite.Height;
  30.    
  31.   if (char_sprite!=null) char_sprite.Delete();
  32.  
  33. // Get speach position & dimensions
  34.   int sayx;
  35.   int sayy;
  36.   sayx=this.x;
  37.   sayy=this.y-(char_height*this.Scaling/100)-this.z;
  38.  
  39.  
  40.  
  41.   wid=GetTextWidth(message, eFontSpeechOutline);
  42.  
  43.  
  44.   savewid=wid;
  45.   newwid = maxwid;
  46.   if (this.x<leftside+GetViewportX()||this.x>rightside+GetViewportX()) newwid=lesswid;
  47.   if (wid>newwid)wid = newwid;
  48.  
  49.   if (this.x-(wid/2)<GetViewportX()) sayx=GetViewportX()+(wid/2);
  50.   if (this.x+(wid/2)>640+GetViewportX()) sayx=640+GetViewportX()-(wid/2);
  51.  
  52.   hei=GetTextHeight(message, eFontSpeechOutline, wid);
  53.   hei+=5;
  54.   if (sayy-hei<8) sayy=hei+8;
  55.  
  56. // Set size of word balloon
  57.   ViewFrame* rectframe;
  58.   DynamicSprite* rectsprite;
  59.  
  60.   rectframe=Game.GetViewFrame(cRectangle.View,cRectangle.Loop, 0);
  61.   rectsprite=DynamicSprite.CreateFromExistingSprite(1116);
  62.   rectsprite.Resize(wid, hei);
  63.   rectframe.Graphic=rectsprite.Graphic;
  64.   if (vOldSprite!=null) vOldSprite.Delete();
  65.   vOldSprite=rectsprite;
  66.  
  67.   if (gHotSpot.Visible) {
  68.     HotsVis=true;
  69.     gHotSpot.Visible=false;
  70.   }
  71.   else {
  72.   HotsVis=false;
  73.   }
  74.  
  75.  
  76.  
  77. // Position message
  78.   cRectangle.Transparency=100;
  79.   cRectangle.ChangeRoom(this.Room, sayx, sayy);
  80.   cRectangle.Baseline=480;
  81.  
  82. // Show message
  83.   cRectangle.Transparency=30;
  84.   this.Say(message);
  85.   cRectangle.Transparency=100;
  86.  
  87.   if (HotsVis) gHotSpot.Visible=true;
  88.  
  89. }
  90.  

The second place dynamic sprites are deleted is in my amended version of BackgroundSpeech where vOldSprite1 and vOldSprite2 are global:
Code: Adventure Game Studio
  1. // Copyright (C) 2007-2009 Tom Vandepoele
  2. //-------------------------------------------------------------------
  3.  
  4. AudioChannel* Channel[];
  5. bool BgTalkActive[];
  6. Overlay* Overlays[];
  7. int Views[];
  8. int Timers[];
  9.  
  10. function game_start()
  11. {
  12.    int ch_idx;
  13.    Channel = new AudioChannel[Game.CharacterCount];
  14.    BgTalkActive = new bool[Game.CharacterCount];
  15.    Overlays = new Overlay[Game.CharacterCount];
  16.    Views = new int[Game.CharacterCount];
  17.    Timers = new int[Game.CharacterCount];
  18.    
  19.    ch_idx = 0;
  20.    while (ch_idx < Game.CharacterCount)
  21.    {
  22.       BgTalkActive[ch_idx] = false;
  23.       ch_idx++;
  24.    }
  25. }
  26.  
  27. // monkey_05_06 ... CreateTextual with Alignment!
  28.  
  29. import Overlay* CreateTextualOverlayAligned(int x, int y, int width, FontType font, int color, String text, Alignment=eAlignCentre);
  30.  
  31. Overlay *textOverlay;
  32.  
  33. Overlay* CreateTextualOverlayAligned(int x, int y, int width, FontType font, int color, String text, Alignment align) {
  34.   int height = GetTextHeight(text, font, width);
  35.   DynamicSprite *sprite = DynamicSprite.Create(width, height);
  36.   DrawingSurface *surface = sprite.GetDrawingSurface();
  37.   surface.DrawingColor = color;
  38.   surface.DrawStringWrapped(0, 0, width, font, align, text);
  39.   surface.Release();
  40.   Overlay *newOverlay = Overlay.CreateGraphical(x, y, sprite.Graphic, true);
  41.   if (sprite!=null) sprite.Delete();
  42.   return newOverlay;
  43. }
  44.  
  45. function ShowTextCentered(String text) {
  46.   int width = GetTextWidth(text, Game.NormalFont);
  47.   int height = GetTextHeight(text, Game.NormalFont, width);
  48.   int x = (System.ViewportWidth / 2) - (width / 2);
  49.   int y = (System.ViewportHeight / 2) - (height / 2);
  50.   textOverlay = CreateTextualOverlayAligned(x, y, width, Game.NormalFont, 17238, text);
  51. }
  52.  
  53. /**
  54. * Have the character say (animation + sound) something in the background.
  55. * Note! The talking character will not have lip sync to either the audio or the text.
  56. **/
  57.  
  58. function StopSayInBackground(this Character*)
  59. {
  60.    if ((BgTalkActive[this.ID]) &&
  61.        (null != Overlays[this.ID]))
  62.    {
  63.       if (null != Channel[this.ID])
  64.       {
  65.          Channel[this.ID].Stop();
  66.       }
  67.      
  68.       if (Overlays[this.ID].Valid)
  69.       {
  70.          Overlays[this.ID].Remove();
  71.       }
  72.      
  73.       if (0 != Views[this.ID])
  74.       {
  75.          this.UnlockView();
  76.       }
  77.      
  78.       BgTalkActive[this.ID] = false;
  79.      
  80.       // MAKE RECTANGLE INVISIBLE
  81.       if (this.GetProperty("background_rect")==1) {
  82.         cRectangle1.Transparency=100;
  83.         if (vOldSprite1!=null) vOldSprite1.Delete();
  84.       }
  85.       else if(this.GetProperty("background_rect")==2) {
  86.         cRectangle2.Transparency=100;
  87.         if(vOldSprite2!=null) vOldSprite2.Delete();
  88.       }
  89.    }
  90.  
  91.  
  92. }
  93.  
  94. function SayInBackground(this Character*,  String message, AudioClip *clip, int view, AudioPriority priority)
  95. {
  96.    this.StopSayInBackground();
  97.    
  98.    int text_width;
  99.  
  100.    text_width = GetTextWidth(message, Game.SpeechFont) * 2;
  101.  
  102.    if (text_width > (System.ViewportWidth /2))
  103.    {
  104.       text_width = System.ViewportWidth /2;
  105.    }
  106.  
  107.  
  108.    int text_height = GetTextHeight(message, Game.SpeechFont, text_width);
  109.  
  110.    
  111.    // Get Character dimensions
  112.    
  113.    int char_width;
  114.    int char_height;
  115.    ViewFrame *char_frame;
  116.    DynamicSprite *char_sprite;
  117.    
  118.    char_frame = Game.GetViewFrame(this.View, this.Loop, this.Frame);
  119.    char_sprite= DynamicSprite.CreateFromExistingSprite(char_frame.Graphic);
  120.    char_width=char_sprite.Width;
  121.    char_height=char_sprite.Height;
  122.    
  123.    if (char_sprite!=null) char_sprite.Delete();
  124.      
  125.    
  126.    /* The x position of the overlay is calculated by the character's sprite width and the text width   */
  127.    /* The y position of the overlay is calculated by the character's sprite height and the text height */
  128.  
  129.    int text_x = this.x  + (char_width / 2) - text_width;
  130.  
  131.    if (text_x + text_width > 631 ) {
  132.      text_x = 631 - text_width;
  133.    }
  134.    else if (text_x < 8 ) {
  135.      text_x = 8;
  136.    }
  137.  
  138.    int text_y = this.y - this.z - (char_height + text_height);
  139.    
  140.    if (text_y < 8){
  141.      text_y = 8;
  142.    }
  143.    else if (text_y + text_height > 471) {
  144.       text_y = 471 - text_height;
  145.    }
  146.  
  147. // GET SIZE AND POSITION OF RECTANGLE, PLACE IN ROOM AND MAKE VISIBLE
  148.    Character* cThisRect;
  149.    int RectNum;
  150.    int ThisSprite;
  151.    ViewFrame* rectframe;
  152.    DynamicSprite* rectsprite;
  153.    if (this.GetProperty("background_rect")==1) {
  154.      cThisRect=cRectangle1;
  155.      ThisSprite=1117;
  156.      RectNum=1;
  157.    }
  158.    else if(this.GetProperty("background_rect")==2) {
  159.      cThisRect=cRectangle2;
  160.      ThisSprite=1118;
  161.      RectNum=2;
  162.    }
  163.    else {
  164.      Display("Background Rectangle for Character SayInBackground is not set or invalid.");
  165.    }
  166.  
  167.  
  168.    rectframe=Game.GetViewFrame(cThisRect.View,cThisRect.Loop, 0);
  169.  
  170.    
  171.    rectsprite=DynamicSprite.CreateFromExistingSprite(ThisSprite);
  172.    rectsprite.Resize(text_width,text_height);
  173.    rectframe.Graphic=rectsprite.Graphic;
  174.    if (RectNum==1) {
  175.      vOldSprite1=rectsprite;
  176.    }
  177.    else {
  178.      vOldSprite2=rectsprite;
  179.    }
  180.    Wait(1);
  181.  
  182.  
  183. // Position message
  184.    cThisRect.Transparency=100;
  185.    cThisRect.ChangeRoom(this.Room,text_x+(text_width/2),text_y+text_height);
  186.    cThisRect.Baseline=480;
  187.  
  188. // Show message
  189.    cThisRect.Transparency=30;
  190.  
  191.    //Overlays[this.ID] = Overlay.CreateTextual(text_x, text_y, text_width, Game.SpeechFont, this.SpeechColor, message);
  192.    Overlays[this.ID] = CreateTextualOverlayAligned(text_x, text_y, text_width, Game.SpeechFont, this.SpeechColor, message, eAlignCentre);
  193.  
  194.    Timers[this.ID] = GetGameSpeed() * (message.Length / 10);
  195.  
  196.    Channel[this.ID] = null;
  197.    if (null != clip)
  198.    {
  199.       Channel[this.ID] = clip.Play(priority);
  200.    }
  201.    
  202.    if (null != Overlays[this.ID])
  203.    {
  204.       Views[this.ID] = view;
  205.       if (0 != view)
  206.       {
  207.          this.LockView(view);
  208.       }
  209.      
  210.       this.Animate(this.Loop, this.SpeechAnimationDelay, eRepeat, eNoBlock);
  211.       BgTalkActive[this.ID] = true;
  212.    }
  213. }
  214.  
  215. function BackgroundTalkActive(this Character*)
  216. {
  217.    return BgTalkActive[this.ID];
  218. }
  219.  
  220. function repeatedly_execute()
  221. {
  222.    int ch_idx = 0;
  223.    
  224.    while (ch_idx < Game.CharacterCount)
  225.    {
  226.       if ((BgTalkActive[ch_idx]) &&
  227.           (null != Overlays[ch_idx]))
  228.       {
  229.          if (Timers[ch_idx] > 0)
  230.          {
  231.             Timers[ch_idx]--;
  232.          }
  233.          
  234.          if (null != Channel[ch_idx])
  235.          {
  236.             if (Channel[ch_idx].PlayingClip == null)
  237.             {
  238.                Channel[ch_idx] = null;
  239.             }
  240.          }
  241.          
  242.          if ((0 == Timers[ch_idx]) &&
  243.              (null == Channel[ch_idx]))
  244.          {
  245.             Overlays[ch_idx].Remove();
  246.            
  247.          // MAKE RECTANGLE INVISIBLE
  248.             if (character[ch_idx].GetProperty("background_rect")==1) {
  249.               cRectangle1.Transparency=100;
  250.               if (vOldSprite1!=null) vOldSprite1.Delete();
  251.             }
  252.             else if(character[ch_idx].GetProperty("background_rect")==2) {
  253.               cRectangle2.Transparency=100;
  254.               if (vOldSprite2!=null) vOldSprite2.Delete();
  255.             }
  256.            
  257.             Overlays[ch_idx] = null;
  258.             if (0 != Views[ch_idx])
  259.             {
  260.                character[ch_idx].UnlockView();
  261.             }
  262.             BgTalkActive[ch_idx] = false;
  263.          }
  264.       }
  265.      
  266.       ch_idx++;
  267.    }
  268. }
  269.  

I expect I have made a beginners error here, but the fact that CJ was taking an interest in this error message a year or so ago prompted me to put it in Advanced. I will get back to putting Deletes where they should be and hope that someone can point out what I've done wrong here.
Incidentally, I am using the Credits and Flashlight modules as well but I haven't tampered with them (much) so I expect the problem doesn't lie there.

Thanks in advance for your help.
« Last Edit: 05 Nov 2012, 19:49 by Hernald »

On second thoughts; I think I will wait to see what people say about the code I have posted before adding any more Deletes.

I didn't look at the second piece of code but what I noticed is that you can replace this:
Code: Adventure Game Studio
  1.   char_frame = Game.GetViewFrame(this.View, this.Loop, this.Frame);
  2.   char_sprite= DynamicSprite.CreateFromExistingSprite(char_frame.Graphic);
  3.   char_width=char_sprite.Width;
  4.   char_height=char_sprite.Height;
  5.   if (char_sprite!=null) char_sprite.Delete();

with this:
Code: Adventure Game Studio
  1.   char_frame = Game.GetViewFrame(this.View, this.Loop, this.Frame);
  2.   char_width = Game.SpriteWidth[char_frame.Graphic];
  3.   char_height = Game.SpriteHeight[char_frame.Graphic];

That should reduce the number of DynSprites being created and deleted significantly.

Thanks for that pointer Khris, I'll get onto it.

... That made my code more efficient, unfortunately it didn't stop my game from crashing. This time it wasn't particularly close to a Changeview.
I will keep trying things out, let me know if there's anything else I can tell you.
« Last Edit: 05 Nov 2012, 23:33 by Hernald »

Could the problem be caused by making a Character's View out of dynamic sprites?

I am actually wondering, what happens after you delete the Old Sprites. You don't seem to restore View Frame's graphic value to original then.
I am thinking, what if AGS tries to remove the Frame's sprite from cache while it was already deleted (as being a dynamic sprite)...
What will happen if you change the Rectangle's View to default (static) sprites before deleting dynamic ones?
(I am speaking about StopSayInBackground function)

Good idea! I'll try that and let you know how it goes.

... I just played through my game without it crashing!
This was how I changed the code:
Code: Adventure Game Studio
  1.       // MAKE RECTANGLE INVISIBLE
  2.       if (this.GetProperty("background_rect")==1) {
  3.         cRectangle1.Transparency=100;
  4.         tempvf = Game.GetViewFrame(cRectangle1.View, cRectangle1.Loop, cRectangle1.Frame);
  5.         tempvf.Graphic=1117;
  6.         if (vOldSprite1!=null) vOldSprite1.Delete();
  7.       }
  8.       else if(this.GetProperty("background_rect")==2) {
  9.         cRectangle2.Transparency=100;
  10.         tempvf = Game.GetViewFrame(cRectangle2.View, cRectangle2.Loop, cRectangle2.Frame);
  11.         tempvf.Graphic=1118;
  12.         if(vOldSprite2!=null) vOldSprite2.Delete();
  13.       }
  14.  
Thanks for the advice Crimson Wizard.
I will play the game through again two or three times in the next week and if all's well I'll mark this as solved.
« Last Edit: 06 Nov 2012, 13:07 by Hernald »

That's very good.

I'd say we uncovered a mistake in AGS engine here. It should behave differently, probably remove dynamic sprite from cache when it is deleted, and change Frame's graphic to 0 for safety reasons. Gonna write this down...
E: added issue: http://www.adventuregamestudio.co.uk/forums/index.php?issue=362.0
« Last Edit: 07 Nov 2012, 00:21 by Crimson Wizard »

Calin Leafshade

  • Long live King Cat!
    • I can help with making music
    • I can help with voice acting
    • Calin Leafshade worked on one or more games that won an AGS Award!
    •  
    • Calin Leafshade worked on one or more games that was nominated for an AGS Award!
I've played around with using dynamicsprites in character views a couple of times and it is very unstable but I could never pin down the root cause. Definitely something to investigate.