cuberite/cuberite

cRoot:ForEachPlayer does not report players that haven't been spawned yet

sleirsgoevy opened this issue · 2 comments

Client version: 1.8.9
Server OS: Linux
Cuberite Commit id: ca705be (latest at the time of posting)

If a player has already joined, but hasn't been spawned into a world yet, cRoot:ForEachPlayer does not iterate over them.

Expected behavior

The callback is called for the player.

Actual behavior

The callback is only called for spawned players.

Steps to reproduce the behavior

Use a slow enough HDD and start the server after a cold boot. I'm not sure how to reproduce this reliably.

Server log

[e183258daeca7b2c|22:39:53] Player "sleirsgoevy2" (200ca00062743a1a8b8861272ca8a214) save or statistics file not found, resetting to defaults
[e183258daeca7b2c|22:39:53] Player "sleirsgoevy2" with save file "players/20/0ca000-6274-3a1a-8b88-61272ca8a214.json" is spawning at {-0.00, 181.00, -1.70} in world "world"
[e183258daeca7b2c|22:39:53] Player sleirsgoevy2 has joined the game
StartGame: uuid = f27ab396f620382b8f774f975552102c, name = sleirsgoevy
 => is lonely
[e183258daeca7b2c|22:39:53] LUA: Plugins/myPlugin/game.lua:108: n_players = 1, num_players = 2
[e183258daeca7b2c|22:39:53] Stack trace:
[e183258daeca7b2c|22:39:53]   [C](-1): error
[e183258daeca7b2c|22:39:53]   Plugins/myPlugin/game.lua(108): StartGame
[e183258daeca7b2c|22:39:53]   Plugins/myPlugin/main.lua(45): (no name)
[e183258daeca7b2c|22:39:53] Stack trace end
[e183258daeca7b2c|22:39:53] LUA: BreakIntoDebugger() not found / not a function
[e183258daeca7b2c|22:39:53] Error in <attached> calling function <callback>()
[feea04b9f30180cb|22:39:53] Adding player sleirsgoevy2 to world "world".
[feea04b9f30180cb|22:39:53] Spawning player "sleirsgoevy2" on client "sleirsgoevy" @ ::ffff:127.0.0.1
[feea04b9f30180cb|22:39:53] sleirsgoevy2 is requesting ViewDistance of 10!

Note: StartGame here is being called from a tick callback, not from the PLAYER_JOINED callback.

cRoot:ForEachPlayer indeed check for each player per world so if the player is not in the world, it wouldn't call.

cuberite/src/Root.cpp

Lines 681 to 691 in ca705be

bool cRoot::ForEachPlayer(cPlayerListCallback a_Callback)
{
for (auto & Entry : m_WorldsByName)
{
if (!Entry.second.ForEachPlayer(a_Callback))
{
return false;
}
}
return true;
}

can you give us a sample of your code ? I don't see why it give's an error, it should just ignore it I guess ?

TL;DR: I added an explicit assert that the number of callback calls equals the number reported by cRoot:GetNumPlayers().

I was troubleshooting an issue in my mini-game plugin, that upon starting a game some players were not assigned into teams. Per the plugin logic, 1 tick after the last player has joined and GetNumPlayers reports that the server is full, ForEachPlayer runs and assigns players into teams. Due to this bug, some players were not assigned into any team.

A workaround I've found is the following:

  1. For each player join, only record a join when the SPAWNED callback is called for that player.
  2. Do not use GetNumPlayers to get the number of players, instead count the players manually using cRoot:ForEachPlayer.