Facebook Twitter YouTube Frictional Games | Forum | Privacy Policy | Dev Blog | Dev Wiki | Support | Gametee


Thread Rating:
  • 5 Vote(s) - 4.2 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Amnesia: The Second Dimension [READY FOR LAUNCH]
Romulator Offline
Not Tech Support ;-)

Posts: 3,628
Threads: 63
Joined: Jan 2013
Reputation: 195
#51
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Do these loops run on a timer? If so, maybe you're able to stop the timer in OnLeave() and restart it in OnBegin()? This may allow the Engine a moment to slow down and readjust itself. Over time, a timer, even if it is set to update every 1/10ths of a second, it will fail to catch up and either try to fix itself or say "I need more memory" which may be also what is happening. (I've noticed this in a few apps I make in VB.Net.) The only problem is if these timer's update 'global' things, which you'll have to store before you stop the Timer.

If you've already done this though, disregard Rolleyes

Discord: Romulator#0001
[Image: 3f6f01a904.png]
(This post was last modified: 02-22-2015, 05:39 AM by Romulator.)
02-22-2015, 05:38 AM
Find
Daemian Offline
Posting Freak

Posts: 1,129
Threads: 42
Joined: Dec 2012
Reputation: 49
#52
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Hey, listen, I just tested this timer error on a new cs, empty level.
I reproduced these timers there plus a counter var, and the results are these:

Aprox. at 65,000 timer calls the game crashes. Not right away, just when you attempt to load another level (or the same).

The error it's always the same, an access violation
Spoiler below!
[Image: 1zp76rt.jpg]
Which it's when it tries to access something that doesn't belong to it. -according to internet-

The curious thing is, this number 65,000 is very close to the data type uint16 (mentioned here.)

So maybe the game is trying to control/organize his timers using an array that its
index is an uint16 data type. (65,535 max.)
And the crash happens when it tries to load back that data, one that was stored beyond that limit (and failed).

Anyone can confirm any of this? I'm just guessing.

(This post was last modified: 02-22-2015, 07:09 AM by Daemian.)
02-22-2015, 07:04 AM
Find
MrBehemoth Offline
Senior Member

Posts: 408
Threads: 19
Joined: Feb 2014
Reputation: 40
#53
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

(02-22-2015, 04:10 AM)Mudbill Wrote: Yeah, perhaps I should join in on this testing and experience the crashes myself. That'll probably give me a better idea. PM?

I'll PM you and Amn shortly, just uploading the "custom stories" version.

(02-22-2015, 05:38 AM)Romulator Wrote: Do these loops run on a timer? If so, maybe you're able to stop the timer in OnLeave() and restart it in OnBegin()? This may allow the Engine a moment to slow down and readjust itself. Over time, a timer, even if it is set to update every 1/10ths of a second, it will fail to catch up and either try to fix itself or say "I need more memory" which may be also what is happening. (I've noticed this in a few apps I make in VB.Net.) The only problem is if these timer's update 'global' things, which you'll have to store before you stop the Timer.

If you've already done this though, disregard Rolleyes

Yeah, that's about right Rom, but I've already done it. All looping timers are stopped and the engine is given a few seconds to catch up with itself before changing maps.

(02-22-2015, 07:04 AM)Daemian Wrote: Hey, listen, I just tested this timer error on a new cs, empty level.
I reproduced these timers there plus a counter var, and the results are these:

Aprox. at 65,000 timer calls the game crashes. Not right away, just when you attempt to load another level (or the same).

The error it's always the same, an access violation
Spoiler below!
[Image: 1zp76rt.jpg]
Which it's when it tries to access something that doesn't belong to it. -according to internet-

The curious thing is, this number 65,000 is very close to the data type uint16 (mentioned here.)

So maybe the game is trying to control/organize his timers using an array that its
index is an uint16 data type. (65,535 max.)
And the crash happens when it tries to load back that data, one that was stored beyond that limit (and failed).

Anyone can confirm any of this? I'm just guessing.

Good thinking! It's go to be this, it makes perfect sense. This is pretty much what I already suspected (some memory range is overflowing), but I never thought of counting the loops. It would also explain why it's not directly related to system memory and why it lasts a bit less that 10 min, whatever levels I playtest. Some levels seem to bring it on slightly earlier, and come to think of it, those are levels that use lots of timers themselves.

I guess that each iteration of a timer is given a unique index as a uint16. It would make sense as FG would never have needed 65,535 timers, and would not have suspected that a modder would need even more.

Now, if this is the case, it's kind of a brick wall. Even if I consolidated the 3 loops into one loop that only runs 30 times a second (which makes the player character look awful) and had no other timers (which would be difficult if not impossible, and would severely limit gameplay options) then the engine would give up just short of 20 minutes in.

I can't think of a way of doing this without this many timers. The only alternative would be functions that don't exist, or if FG release an update that uses uint/uint32 to index its timers... and that's not gonna happen.

02-22-2015, 12:37 PM
Find
Mudbill Offline
Muderator

Posts: 3,881
Threads: 59
Joined: Apr 2013
Reputation: 179
#54
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Hmm, yes that does seem like a likely diagnose of the problem at hand. I think we should at least attempt to contact someone on the inside of the engine. We can at least get some confirmations.

- Is it true that the timer uses uint16 for indexing?
- Is the engine dependent on this value or is it just for the sake of not needing a higher value?
- Would the engine still run the same if it was upgraded?
- Would upgrading it be possible?
- If not, would it be possible to implement a sortof FlushTimers function that empties this index?

If we could have some help from FG, maybe we can solve this issue. You might be right that it's not gonna happen, but we should surely give it a thorough try! Something as amazing as this would be terrible if it had to go to waste.

02-22-2015, 02:58 PM
Find
MrBehemoth Offline
Senior Member

Posts: 408
Threads: 19
Joined: Feb 2014
Reputation: 40
#55
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Good call. I've PM'd Jens, Luis and Thomas.

02-22-2015, 04:07 PM
Find
Daemian Offline
Posting Freak

Posts: 1,129
Threads: 42
Joined: Dec 2012
Reputation: 49
#56
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Here's the small cs where I tested.
You can launch it as a normal cs, you'll immediately see via debug messages a value that starts increasing, that's the timers' counter.

When you see it reaches the number 50k, it is safe to exit the game and/or load another level, etc. But after 60k~65k it crashes when you try to change the level.

I tried diff ways of removing the timer using RemoveTimer. And no luck.
I put it inside the function, OnLeave, right after it runs, calling the timers with diff names, etc.
Nothing works, no matter what/how you use the timers, at 60k times you used them, it crashes.

Oh an another thing I realized, the crash happens when it unloads, not when it loads another level.
You can tell because it crashes too when you exit the game.


Attached Files
.zip   test111.zip (Size: 707.63 KB / Downloads: 110)

(This post was last modified: 02-22-2015, 04:49 PM by Daemian.)
02-22-2015, 04:10 PM
Find
MrBehemoth Offline
Senior Member

Posts: 408
Threads: 19
Joined: Feb 2014
Reputation: 40
#57
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

(02-22-2015, 04:10 PM)Daemian Wrote: Oh an another thing I realized, the crash happens when it unloads, not when it loads another level.
You can tell because it crashes too when you exit the game.

I wondered about that. I noticed like you that it occurs on exiting the game, but what confuses matters is that, when it crashes on loading a new map, the log shows that the new map has loaded. Not sure if that means anything though, whatever process tries to access an out of range timer and causes the crash could be asynchronous.

02-22-2015, 05:45 PM
Find
Mudbill Offline
Muderator

Posts: 3,881
Threads: 59
Joined: Apr 2013
Reputation: 179
#58
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

I guess it's also worth noting that timers keep running even if the map no longer is loaded. So with that I'm fairly certain they are still saved even if they have expired. RemoveTimer instantly expires it.

Have you tried using the ClearSavedMaps(); and DestroyDataCache(); functions? Do you think maybe they could "release" these values?

(This post was last modified: 02-22-2015, 06:32 PM by Mudbill.)
02-22-2015, 06:29 PM
Find
Daemian Offline
Posting Freak

Posts: 1,129
Threads: 42
Joined: Dec 2012
Reputation: 49
#59
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

Hey, wait, I found another way to loop a function without timers!
This seems to be looping once every 0.01 seconds. (100 times per second)
Is that an acceptable number for your game Behe? Or you need it to loop faster?

I'm editing in a second with the code in case you wanna give it a try.

02-22-2015, 07:24 PM
Find
MrBehemoth Offline
Senior Member

Posts: 408
Threads: 19
Joined: Feb 2014
Reputation: 40
#60
RE: Amnesia: The Second Dimension [SERIOUSLY?!?!]

(02-22-2015, 06:29 PM)Mudbill Wrote: I guess it's also worth noting that timers keep running even if the map no longer is loaded. So with that I'm fairly certain they are still saved even if they have expired. RemoveTimer instantly expires it.

Have you tried using the ClearSavedMaps(); and DestroyDataCache(); functions? Do you think maybe they could "release" these values?

Here are the functions that run when you walk into an "ExitArea":

void OnExitShutDown(string &in asTimer)
{
    PlayerActorShutDown();
    NpcShutDown();
    RemoveTimer("LevelLoopTimer");
    RemoveTimer("CutsceneTimer");
    AddTimer("ExitTimer", 1.5f, "OnExitChangeMap");
}

void OnExitChangeMap(string &in asTimer)
{
    if(GetGlobalVarInt("ScoreboardDisabled") == 1)
    {
        ClearSavedMaps();
        Print("SECOND DIMENSION: Scoreboard skipped, start credits " + GetGlobalVarString("NextLevel") + "\n");
        StartCredits("", false, "LevelEnd", "LevelEnd_" + GetGlobalVarInt("LevelNum"), -1);
    }
    else
    {
        SetupLoadScreen("", "", 0, "");
        Print("SECOND DIMENSION: loading scoreboard\n");
        ChangeMap("SecondDimenion/maps/scoreboard.map", "PlayerStart_Start", "", "");
    }
}

// ...

void PlayerActorShutDown()
{
    Print("SECOND DIMENSION: PlayerActorShutDown()\n");
    RemoveTimer("PlayerActorTimer");
    RemoveTimer("GasHeadWobbleTimer");
    RemoveTimer("PlayerInteractTimer");
    RemoveTimer("PlayerInteractHitTimer");
    SetPlayerActive(false);
    SetEntityActive("PlayerActor_Left", false);
    SetEntityActive("PlayerActor_Right", false);
}

// ...

void NpcShutDown()
{
    Print("SECOND DIMENSION: NpcShutDown()\n");
    RemoveTimer("NpcActionTimer");
    RemoveTimer("NpcModelTimer");
    NpcQueueClear();
    int iNpc;
    for(iNpc = 0; iNpc < NpcsInitialised; iNpc++)
    {
        NpcState[iNpc] = 0;
        if(GetEntityExists(NpcName[iNpc] + "_Front"))
        {
            SetEntityActive(NpcName[iNpc] + "_Front", false);
        }
        if(GetEntityExists(NpcName[iNpc] + "_Back"))
        {
            SetEntityActive(NpcName[iNpc] + "_Back", false);
        }
        if(GetEntityExists(NpcName[iNpc] + "_Left"))
        {
            SetEntityActive(NpcName[iNpc] + "_Left", false);
        }
        if(GetEntityExists(NpcName[iNpc] + "_Right"))
        {
            SetEntityActive(NpcName[iNpc] + "_Right", false);
        }
    }
    NpcsInitialised = 0;
    NpcScriptOverride = true;
}


So, all the looping timers, and other timers besides, are removed. It seems that RemoveTimer() expires the timer but it something doesn't free up, possibly just a unique index, and contributes to the 65,535 limit.

I didn't try DestroyDataCache(), as I think that's just for assets cached with CreateDataCache().

02-22-2015, 07:36 PM
Find




Users browsing this thread: 1 Guest(s)