@@ -56,78 +56,61 @@ abstract class EvaluationMixinState {
5656
5757/// A mixin to provide engine evaluation functionality to an [AsyncNotifier] .
5858///
59- /// The parent must initialize the engine evaluation by calling [initEngineEvaluation] and dispose it
60- /// by calling [disposeEngineEvaluation] .
61- ///
6259/// The parent must implement the following:
63- /// - [evaluationState]
6460/// - [socketClient] to provide the [SocketClient] to use for cloud evaluations. If `null` , the
6561/// cloud evaluations will not be requested.
6662/// - [positionTree] to provide the tree where the evaluations are stored.
67- /// - [evaluationServiceFactory]
68- /// - [evaluationPreferencesNotifier]
69- /// - [evaluationPrefs]
7063///
7164/// The parent can implement:
7265/// - [onCurrentPathEvalChanged] to refresh the current node after an evaluation.
73- mixin EngineEvaluationMixin {
74- /// Direct access to underlying [EvaluationMixinState] .
75- ///
76- /// This is a workaround to use this mixin in both [Notifier] and [AsyncNotifier] .
77- /// Parent must ensure that [AsyncNotifier.state] is loaded before using methods that require
78- /// [EvaluationMixinState] .
79- EvaluationMixinState get evaluationState;
80-
81- EngineEvaluationPrefState get evaluationPrefs;
82- EngineEvaluationPreferences get evaluationPreferencesNotifier;
83- EvaluationService evaluationServiceFactory ();
66+ mixin EngineEvaluationMixin <T extends EvaluationMixinState > on AnyNotifier <AsyncValue <T >, T > {
67+ late EvaluationService _evaluationService;
68+
8469 SocketClient ? get socketClient;
8570 Node get positionTree;
8671
72+ EngineEvaluationPrefState get evaluationPrefs => ref.read (engineEvaluationPreferencesProvider);
73+
74+ EngineEvaluationPreferences get _evaluationPreferencesNotifier =>
75+ ref.read (engineEvaluationPreferencesProvider.notifier);
76+
8777 final _evalRequestDebounce = Debouncer (kRequestEvalDebounceDelay);
8878 final _localEngineAfterDelayDebounce = Debouncer (kLocalEngineAfterCloudEvalDelay);
8979
9080 StreamSubscription <SocketEvent >? _socketSubscription;
9181
92- EvaluationService ? _evaluationService;
93-
9482 /// Called when a received evaluation is for the current path.
9583 ///
9684 /// If the evaluation string is the same for both the received and the current evaluation, the
9785 /// [isSameEvalString] parameter will be `true` . It can be used to avoid refreshing the UI if the
9886 /// evaluation string is the same.
9987 void onCurrentPathEvalChanged (bool isSameEvalString) {}
10088
101- /// Initializes the engine evaluation.
102- ///
103- /// Will start listening to the [SocketClient] for cloud evaluations.
104- ///
105- /// The local engine is not started here, but only when [requestEval] is called.
106- @nonVirtual
107- void initEngineEvaluation () {
108- _socketSubscription = socketClient? .stream.listen (_handleSocketEvent);
109- _evaluationService = evaluationServiceFactory ();
110- }
89+ @override
90+ void runBuild () {
91+ _evaluationService = ref.read (evaluationServiceProvider);
11192
112- /// Disposes all resources related to the engine evaluation.
113- @nonVirtual
114- void disposeEngineEvaluation () {
115- _evalRequestDebounce.cancel ();
116- _localEngineAfterDelayDebounce.cancel ();
117- _socketSubscription? .cancel ();
118- _evaluationService? .disposeEngine ();
119- _evaluationService = null ;
93+ ref.onDispose (() {
94+ _evalRequestDebounce.cancel ();
95+ _localEngineAfterDelayDebounce.cancel ();
96+ _socketSubscription? .cancel ();
97+ _evaluationService.disposeEngine ();
98+ });
99+
100+ super .runBuild ();
101+
102+ _socketSubscription = socketClient? .stream.listen (_handleSocketEvent);
120103 }
121104
122105 /// Toggles the engine evaluation on/off.
123106 @mustCallSuper
124107 Future <void > toggleEngine () async {
125- await evaluationPreferencesNotifier .toggle ();
108+ await _evaluationPreferencesNotifier .toggle ();
126109
127- if (evaluationState .isEngineAvailable (evaluationPrefs)) {
110+ if (state.requireValue .isEngineAvailable (evaluationPrefs)) {
128111 requestEval (forceRestart: true );
129112 } else {
130- await _evaluationService? .disposeEngine ();
113+ await _evaluationService.disposeEngine ();
131114 }
132115 }
133116
@@ -137,33 +120,35 @@ mixin EngineEvaluationMixin {
137120 positionTree.updateAll ((node) => node.eval = null );
138121 onCurrentPathEvalChanged (false );
139122
140- evaluationPreferencesNotifier .setNumEvalLines (numEvalLines);
123+ _evaluationPreferencesNotifier .setNumEvalLines (numEvalLines);
141124
142- _evaluationService? .options = evaluationPrefs.evaluationOptions;
125+ _evaluationService.options = evaluationPrefs.evaluationOptions;
143126
144127 requestEval (forceRestart: true );
145128 }
146129
147130 @mustCallSuper
148131 void setEngineCores (int numEngineCores) {
149- evaluationPreferencesNotifier .setEngineCores (numEngineCores);
132+ _evaluationPreferencesNotifier .setEngineCores (numEngineCores);
150133
151- _evaluationService? .options = evaluationPrefs.evaluationOptions;
134+ _evaluationService.options = evaluationPrefs.evaluationOptions;
152135
153136 requestEval (forceRestart: true );
154137 }
155138
156139 @mustCallSuper
157140 void setEngineSearchTime (Duration searchTime) {
158- evaluationPreferencesNotifier .setEngineSearchTime (searchTime);
141+ _evaluationPreferencesNotifier .setEngineSearchTime (searchTime);
159142
160- _evaluationService? .options = evaluationPrefs.evaluationOptions;
143+ _evaluationService.options = evaluationPrefs.evaluationOptions;
161144
162145 requestEval (forceRestart: true );
163146 }
164147
165148 /// Requests an engine evaluation if available.
166149 ///
150+ /// This must be called after the AsyncNotifier's[state] has been initialized.
151+ ///
167152 /// This sends an `evalGet` event to the server to get the cloud evaluation and starts the local
168153 /// engine evaluation.
169154 ///
@@ -177,10 +162,10 @@ mixin EngineEvaluationMixin {
177162 /// moves.
178163 @nonVirtual
179164 void requestEval ({bool goDeeper = false , bool forceRestart = false }) {
180- if (! evaluationState .isEngineAvailable (evaluationPrefs)) return ;
165+ if (! state.requireValue .isEngineAvailable (evaluationPrefs)) return ;
181166
182167 final delayLocalEngine =
183- evaluationState .alwaysRequestCloudEval &&
168+ state.requireValue .alwaysRequestCloudEval &&
184169 evaluationPrefs.engineSearchTime != kMaxEngineSearchTime;
185170
186171 _evalRequestDebounce (() {
@@ -233,25 +218,28 @@ mixin EngineEvaluationMixin {
233218 node.eval = eval;
234219 });
235220
236- if (evaluationState.currentPath == path) {
221+ if (! ref.mounted) return ;
222+
223+ if (state.requireValue.currentPath == path) {
237224 onCurrentPathEvalChanged (isSameEvalString);
238225 }
239226 }
240227
241228 bool _canCloudEval () {
242- if (evaluationState.currentPosition! .ply >= 15 && ! evaluationState.alwaysRequestCloudEval) {
229+ if (state.requireValue.currentPosition! .ply >= 15 &&
230+ ! state.requireValue.alwaysRequestCloudEval) {
243231 return false ;
244232 }
245- if (positionTree.nodeAt (evaluationState .currentPath).eval is CloudEval ) return false ;
233+ if (positionTree.nodeAt (state.requireValue .currentPath).eval is CloudEval ) return false ;
246234
247235 // cloud eval does not support threefold repetition
248236 final Set <String > fens = < String > {};
249- final nodeList = positionTree.branchesOn (evaluationState .currentPath).toList ();
237+ final nodeList = positionTree.branchesOn (state.requireValue .currentPath).toList ();
250238 for (var i = nodeList.length - 1 ; i >= 0 ; i-- ) {
251239 final node = nodeList[i];
252240 final epd = fenToEpd (node.position.fen);
253241 if (fens.contains (epd)) return false ;
254- if (node.sanMove.isIrreversible (evaluationState .evaluationContext.variant)) {
242+ if (node.sanMove.isIrreversible (state.requireValue .evaluationContext.variant)) {
255243 return true ;
256244 }
257245 fens.add (epd);
@@ -261,30 +249,30 @@ mixin EngineEvaluationMixin {
261249 }
262250
263251 void _sendEvalGetEvent () {
264- if (! evaluationState .isEngineAvailable (evaluationPrefs)) return ;
252+ if (! state.requireValue .isEngineAvailable (evaluationPrefs)) return ;
265253 if (evaluationPrefs.engineSearchTime == kMaxEngineSearchTime) return ;
266254 if (! _canCloudEval ()) return ;
267- final curPosition = evaluationState .currentPosition;
255+ final curPosition = state.requireValue .currentPosition;
268256 if (curPosition == null ) return ;
269257 final numEvalLines = evaluationPrefs.numEvalLines;
270258
271259 socketClient? .send ('evalGet' , {
272260 'fen' : curPosition.fen,
273- 'path' : evaluationState .currentPath.value,
261+ 'path' : state.requireValue .currentPath.value,
274262 'mpv' : numEvalLines,
275263 'up' : true ,
276264 });
277265 }
278266
279267 Future <void > _startEngineEval ({bool goDeeper = false , bool forceRestart = false }) async {
280- final curState = evaluationState ;
268+ final curState = state.requireValue ;
281269 if (! curState.isEngineAvailable (evaluationPrefs)) return ;
282- await _evaluationService? .ensureEngineInitialized (
283- evaluationState .evaluationContext,
270+ await _evaluationService.ensureEngineInitialized (
271+ state.requireValue .evaluationContext,
284272 initOptions: evaluationPrefs.evaluationOptions,
285273 );
286274 _evaluationService
287- ? .start (
275+ .start (
288276 curState.currentPath,
289277 positionTree.branchesOn (curState.currentPath).map (Step .fromNode),
290278 initialPositionEval: positionTree.eval,
@@ -312,7 +300,7 @@ mixin EngineEvaluationMixin {
312300 // if the cloud eval is likely better, stop the local engine
313301 // nps varies with positional complexity so this is rough, but save planet earth
314302 if (likelyNodes < nodeEval.nodes) {
315- _evaluationService? .stop ();
303+ _evaluationService.stop ();
316304 }
317305 return ;
318306 }
@@ -324,11 +312,17 @@ mixin EngineEvaluationMixin {
324312 isSameEvalString = eval.evalString == nodeEval? .evalString;
325313 node.eval = eval;
326314 });
327- if (work.path == evaluationState.currentPath) {
315+
316+ if (! ref.mounted) return ;
317+
318+ if (work.path == state.requireValue.currentPath) {
328319 onCurrentPathEvalChanged (isSameEvalString);
329320 }
330321 });
331322 }
332323
333- bool _shouldEmit (Work work) => work.path == evaluationState.currentPath;
324+ bool _shouldEmit (Work work) {
325+ if (! ref.mounted) return false ;
326+ return work.path == state.requireValue.currentPath;
327+ }
334328}
0 commit comments