Skip to content

fix(follow): never list a follower whose master was refused - #4086

Merged
bylins merged 1 commit into
masterfrom
fix/follower-added-without-master
Oct 20, 2026
Merged

bylins merged 1 commit into
masterfrom
fix/follower-added-without-master

Conversation

@bylins

@bylins bylins commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Причина зависания из #4083. Дополняет #4084: там страховка в цикле, здесь — то, откуда берётся само рассогласование.

Как получается последователь без хозяина

CharData::set_master молча отказывает, если цепочка лидеров замкнулась бы в кольцо:

void CharData::set_master(CharData::ptr_t master) {
	if (follow::MakesLoop(this, master)) {
		follow::ReportLoopError(this, master);
		return;
	}
	m_master = master;
}

А AddFollowerSilently результат не смотрела:

	ch->set_master(leader);

	leader->followers.push_front(ch);   // легло в список в любом случае

Получается ведомый, который числится в свите, но хозяином лидера не считает. Снять его нельзя: StopFollower на таком сразу выходит с stop_follower(...) without master, не тронув список. А DieFollower, разгоняя свиту через while (!ch->followers.empty()), крутится на нём вечно.

Как это легло в боевой лог

19:22:12  Обнаружена ошибка логики: попытка сделать цикл в цепочке последователей.
          Текущая цепочка лидеров: [костяная гончая]
          Потенциальный лидер: name=[Морхель]; адрес 0x5aed82c254d0;
          текущий лидер: name=[костяная гончая]; адрес 0x5aed84f9f480
...
19:25:42  Морхель has rented
19:25:42  [charlife] ExtractCharFromWorld Морхель #-1 room 4056
19:25:42  SYSERR: stop_follower(костяная гончая) without master
19:25:42  SYSERR: stop_follower(костяная гончая) without master
          ... и так до самого gdb

Морхель следовал за костяной гончей; попытка подчинить гончую Морхелю замкнула бы кольцо, set_master отказал — и гончая всё равно оказалась в свите Морхеля без хозяина. Через три минуты Морхель встал на постой, ExtractCharFromWorld позвал DieFollower, и сервер ушёл в вечный цикл. Адрес 0x5aed82c254d0 из этой строки — тот же ch, что в трейсе gen_receptionist из issue.

Правка

AddFollowerSilently возвращает, подчинился ли ведомый, и кладёт его в список только когда хозяин действительно взялся. Проверяется результат, а не условие: так страховка держит и любой будущий отказ внутри set_master, а не только текущую проверку на кольцо.

Заодно AddFollower на отказе молчит — раньше он печатал «Вы начали следовать за ...», даже когда подчинение не состоялось.

Проверка

Сборка -Dbuild_profile=release -Dbuild_tests=true без предупреждений, ./tests/tests — 796 passed, 3 skipped (те же, что и до правки).

🤖 Generated with Claude Code

Нашлась причина зависания из #4083. CharData::set_master молча отказывает,
если цепочка лидеров замкнулась бы в кольцо:

	void CharData::set_master(CharData::ptr_t master) {
		if (follow::MakesLoop(this, master)) {
			follow::ReportLoopError(this, master);
			return;
		}
		m_master = master;
	}

а AddFollowerSilently результат не смотрел и клал ведомого в список лидера
в любом случае. Получался последователь без хозяина: StopFollower такого
снять не может, он сразу выходит с "stop_follower(...) without master", и
DieFollower, разгоняя свиту, крутится на нём вечно.

По боевому логу всё сходится. 19:22:12 -- "попытка сделать цикл в цепочке
последователей", текущий лидер Морхеля костяная гончая, пробуют подчинить
гончую Морхелю. Указатель не взялся, а в свиту Морхеля гончая легла.
19:25:42 -- Морхель встаёт на постой, ExtractCharFromWorld зовёт
DieFollower, и сервер уходит в вечный цикл на этой гончей.

AddFollowerSilently теперь возвращает, подчинился ли ведомый, и в список
кладёт только когда хозяин действительно взялся. AddFollower на отказе
молчит -- раньше он печатал "Вы начали следовать за ...", даже когда
подчинение не состоялось.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@bylins
bylins merged commit 11d8a05 into master Oct 20, 2026
20 checks passed
@bylins
bylins deleted the fix/follower-added-without-master branch October 20, 2026 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant