News:

SMF - Just Installed!

Main Menu

Duel to the Death black screen problem

Started by Moo, June 11, 2013, 03:36:33 PM

Previous topic - Next topic

Moo

Currently a loser in a duel to the death type game on the official server will consistently get a black screen rather than being eliminated, and the game will never end.

With the help of K9 as guinea pig, I managed to locate the cause of the problem...
In Toolbox, init.lua, line 126, kts.SetRespawnFunction(resp).

Commenting out that line, everything works properly again.

Then the question was, is the problem with the toolbox script, or the SetRespawnFunction itself. By trying it with a simple function that always returns nil, and getting the black screen problem, I'd say it's the latter. So, another one for you, Stephen :-X

Anyway... For now, commenting out that line in toolbox would cure the server.

K9

Thank you Moo, I was happy to test with your server, through your debugging of this issue.

It's been very frustrating on Duel to the Death quests lately, without having the proper end-game closure.

Looking forward to Stephen making that tweak to fix the official server for everyone to enjoy.

Great work spotting the cause.

Stephen

First of all, my apologies to anyone who has been affected by this, and thank you to K9 and Moo for tracking down the bug.

I do not know exactly what is going wrong, and unfortunately I don't really have time to investigate further at the moment. I have added a ticket to Trac, will try to come back to it at some future point.

For now I have commented out that line on the server as Moo suggests. I confirmed that this does indeed fix the problem.

ImpassIve

#3
Unfortunately, I haven't yet realized how to build Knights from sources on Windows, so I can't check my suspicion, but:

in src\engine\impl\player.cpp
function bool Player::respawn() looks like this:
____
{
    if (!respawn_func.hasValue() && getRespawnType() != R_RANDOM_SQUARE && (!home_dmap || home_location.isNull())) {
        if (!already_eliminated) {
            //----BRANCH 1----
           //SEND "Player X eliminated" message and execute  mediator.eliminatePlayer(*this);
        }
        return true;  // this counts as a "success" -- we do not want the respawn task to keep retrying.
    }

    // Find out where we should respawn
    if (!respawn_map || respawn_point.isNull())
        return false; //----BRANCH 2----

    // Respawn successful -- Create a new knight.
    //----BRANCH 3----
 
    return true;
}
____

So the Branch 1 (which is responsible for "eliminated" messages) can be reached only if there is no lua respawn function set.
Otherwise, if the respawn_func is set and there is no spawn points left, BRANCH 2 will be executed (just return false without any other actions).
Does that mean what it will keep trying to respawn a player later without any success?

void RespawnTask::execute(TaskManager &tm)
{
    if (this != player.respawn_task.get()) return;

    bool success = player.respawn();
    if (!success) {
        tm.addTask(shared_from_this(), TP_NORMAL, tm.getGVT() + 100); // try again later
    }
}


IMHO, on the one hand  - that would mean what any lua respawn function should somehow detect this situation and call kts.EliminatePlayer() manually (even if it was used just to detect knight respawn event and it always returns nil). On the other hand - I'm not sure how lua function can check whether there are any more spawn points left (haven't used lua for a long time)




ImpassIve

#4
Just tried this on a recently compiled Linux build with Toolbox mod installed.
Current version: black screen for loser in Duel to Death.
After commenting out /*!respawn_func.hasValue() &&*/ part: everything is OK.

Of course, it's too rough solution, since the game should really check whether the user-defined respawn function is present (otherwise, there would be problems with 'custom maps' without spawn points or something like this).
But, maybe, it should somehow distinguish "return nil" case and real custom respawn function (which would choose a spawn points for knights)?




ImpassIve

#5
Well... I've tried to solve this problem on a mod's side.
Modified my respawn function like this:
(1)

...
local home = kts.GetHomeFor(player)
if (home == nil) or ((home.secured_by ~= nil) and (home.secured_by ~= player)) then
    kts.EliminatePlayer(player)
end
...


Aaand... nothing changed. Still a black screen for loser.
I've tried adding debug output and modifying this code, so:

(2)

...
local home = kts.GetHomeFor(player)
if (home == nil) or (home.secured_by ~= nil)) then
    kts.EliminatePlayer(player)
end
...

^ works fine. Pick up a wand, secure your own spawn point, suicide - you are eliminated.

But I don't quite understand the returned value of the kts.GetHomeFor(player) function.
For example, even if all homes on a map were destroyed (by double securing them) - it will return some non-nil value.


Upd: tried a more detailed output to see which one.

Well, I suppose, the 'tile' field would solve the problem (as it is nil only if the player's last spawn door was secured)...




Stephen

Hi Impassive, thanks for your detailed investigation.

That respawn function logic does seem broken.

I think originally I designed the respawn_function for use in the Tutorial, which wants to customize the respawn point depending on where you are. (For example if you die in the Chamber of Bats we want to respawn the player in the same room, not send them all the way back to the beginning.)

I didn't anticipate your use case, where you just want to be notified when a respawn happens, without changing the respawn behaviour at all.

Anyway I would suggest we change the definition of the respawn_func to be as follows:


  • If the respawn func returns nil, then use the default respawn behaviour (i.e., respawn at the player's normal entry point, unless all homes have been secured, in which case eliminate the player.)
  • If the respawn function returns a position and a direction, then respawn the player at that position (this is the current behaviour).
  • If the respawn function returns the string "retry", then do not respawn the knight now, instead wait a while and try again later. (This is the current behaviour if you return nil.)

Then you could return nil in your respawn function and it would work as you expect.

Does that sound ok?

Also, regarding kts.GetHomeFor: I think that is just a bug. In the C++ code, if no home exists, the home position is set to (-2,-2) (which is outside the map). That is what you are seeing in your lua code. However, kts.GetHomeFor is supposed to return nil in this case, so I will change it to do that.

ImpassIve

#7
Quote from: Stephen on June 27, 2014, 12:46:36 PMDoes that sound ok?
Of course, thank you




Stephen

OK, I have now made the above changes.

It does seem to have fixed the "black screen problem", which is good :)

Please note that kts.GetHomeFor can now return nil, so if you are using that function, you may need to update your code to check for a nil result.