2929import com .teammoeg .frostedheart .content .town .ITownWithResources ;
3030import com .teammoeg .frostedheart .content .town .building .AbstractTownBuilding ;
3131import com .teammoeg .frostedheart .content .town .provider .ITownProviderSerializable ;
32+ import com .teammoeg .frostedheart .content .town .resource .TeamTownResourceActionExecutorHandler ;
33+ import com .teammoeg .frostedheart .content .town .resource .TeamTownResourceHolder ;
3234import com .teammoeg .frostedheart .content .town .resource .action .TownResourceActions ;
35+ import com .teammoeg .frostedheart .content .town .resource .watcher .IWarehouseStockWatcher ;
36+ import com .teammoeg .frostedheart .content .town .resource .watcher .IWarehouseStockWatcherNode ;
3337import net .minecraft .core .BlockPos ;
3438import net .minecraft .core .Direction ;
3539import net .minecraft .nbt .CompoundTag ;
6266 * warehouse interface's redstone control mode it enables level-emitter-controlled
6367 * warehouse output.
6468 */
65- public class WarehouseLevelEmitterBlockEntity extends CBlockEntity implements CTickableBlockEntity , MenuProvider {
69+ public class WarehouseLevelEmitterBlockEntity extends CBlockEntity implements IWarehouseStockWatcherNode , MenuProvider {
6670 public static final int STATUS_UNBOUND = 0 ;
6771 public static final int STATUS_UNAVAILABLE = 1 ;
6872 public static final int STATUS_WORKING = 2 ;
6973
70- private final LazyTickWorker refreshWorker = new LazyTickWorker (10 , this ::refreshState );
7174 @ Nullable
7275 private SimpleItemKey filter ;
7376 private int threshold = 1 ;
@@ -78,11 +81,16 @@ public class WarehouseLevelEmitterBlockEntity extends CBlockEntity implements CT
7881 private ITownProviderSerializable <? extends ITownWithBuildings > townProvider ;
7982 private BlockPos warehousePos ;
8083 private int connectionStatus = STATUS_UNBOUND ;
84+ // 由资源持有者分配的 watcher,用于精准订阅物品数量变化
85+ @ Nullable
86+ private IWarehouseStockWatcher watcher ;
8187
88+ //构造器
8289 public WarehouseLevelEmitterBlockEntity (BlockPos pos , BlockState state ) {
8390 super (FHBlockEntityTypes .WAREHOUSE_LEVEL_EMITTER .get (), pos , state );
8491 }
8592
93+ // --- getters ---
8694 @ Nullable
8795 public SimpleItemKey getFilter () {
8896 return filter ;
@@ -108,16 +116,44 @@ public int getConnectionStatus() {
108116 return connectionStatus ;
109117 }
110118
119+
120+ // ---------- IWarehouseStockWatcherNode 实现 ----------
121+
111122 public void setFilter (@ Nullable SimpleItemKey newFilter ) {
123+ SimpleItemKey oldFilter = this .filter ;
112124 this .filter = newFilter ;
113125 if (level != null ) {
114126 setChanged ();
115127 if (!level .isClientSide ) {
116- refreshWorker .enqueue ();
128+ if (watcher != null ) {
129+ configureWatcher (); // 自动重新 add/remove
130+ }
131+ refreshState (); // 立即查一次库存更新红石
117132 }
118133 }
119134 }
120135
136+ @ Override
137+ public void updateWatcher (IWarehouseStockWatcher newWatcher ) {
138+ this .watcher = newWatcher ;
139+ configureWatcher ();
140+ }
141+
142+ @ Override
143+ public void onStockChange (SimpleItemKey item , long newAmount ) {
144+ if (level == null || level .isClientSide ) return ;
145+ lastKnownStock = newAmount ;
146+ boolean on = (mode == WarehouseRedstoneMode .LOW_SIGNAL ) == (newAmount < threshold );
147+ setEmitterOn (on , newAmount );
148+ }
149+
150+ private void configureWatcher () {
151+ if (watcher == null ) return ;
152+ watcher .reset ();
153+ if (filter != null ) {
154+ watcher .addWatch (filter );
155+ }
156+ }
121157 public void setFilterFromStack (ItemStack stack ) {
122158 if (!stack .isEmpty ()) {
123159 setFilter (SimpleItemKey .from (stack ));
@@ -128,22 +164,20 @@ public void setThreshold(int newThreshold) {
128164 this .threshold = Math .max (1 , newThreshold );
129165 if (level != null ) {
130166 setChanged ();
131- if (!level .isClientSide ) {
132- refreshWorker .enqueue ();
133- }
167+ if (!level .isClientSide ) refreshState ();
134168 }
135169 }
136170
137171 public void cycleMode () {
138172 this .mode = this .mode .nextEmitterMode ();
139173 if (level != null ) {
140174 setChanged ();
141- if (!level .isClientSide ) {
142- refreshWorker .enqueue ();
143- }
175+ if (!level .isClientSide ) refreshState ();
144176 }
145177 }
146178
179+ // ---------- 绑定管理 ----------
180+
147181 /**
148182 * Claims this emitter for a warehouse. A still-valid binding owned by a
149183 * different warehouse is never stolen.
@@ -157,16 +191,22 @@ public boolean tryBind(ITownProviderSerializable<? extends ITownWithBuildings> p
157191 return true ;
158192 }
159193 if (warehousePos != null && resolveBinding (false ).isPresent ()) {
194+ // 旧绑定仍有效,不抢夺
160195 return false ;
161196 }
162197
198+ // 清理旧绑定(包括旧的 watcher)
199+ clearBinding ();
200+
163201 this .townProvider = provider ;
164202 this .warehousePos = newWarehousePos .immutable ();
165203 this .connectionStatus = STATUS_UNAVAILABLE ;
166204 if (level != null ) {
167205 setChanged ();
168206 }
169- refreshWorker .enqueue ();
207+
208+ // 获取新的 watcher 并配置
209+ refreshWatcherAndState ();
170210 return true ;
171211 }
172212
@@ -215,6 +255,12 @@ private Optional<BindingContext> invalidBinding(boolean clearWhenInvalid) {
215255 }
216256
217257 private void clearBinding () {
258+ // 释放 watcher,它会自动从资源持有者的索引中清除
259+ if (watcher != null ) {
260+ watcher .reset ();
261+ watcher = null ;
262+ }
263+
218264 boolean changed = townProvider != null || warehousePos != null ;
219265 townProvider = null ;
220266 warehousePos = null ;
@@ -225,26 +271,48 @@ private void clearBinding() {
225271 }
226272
227273 /**
228- * 查询城镇库存并刷新输出状态。状态翻转时通知相邻方块及正面方块的邻居,
229- * 与 AE2 发信器的邻居通知行为一致。
230- * <p>
231- * Polls the town stock and refreshes the output state. On a state flip, neighbors
232- * of this block and of the block in front are notified, like the AE2 level emitter.
274+ * 与仓库建立新的 Watcher 订阅,并且刷新一次当前库存状态。
233275 */
234- private void refreshState () {
235- if (level == null || level .isClientSide ) {
276+ private void refreshWatcherAndState () {
277+ if (level == null || level .isClientSide ) return ;
278+
279+ Optional <BindingContext > binding = resolveBinding (false );
280+ if (binding .isEmpty ()) {
281+ connectionStatus = STATUS_UNBOUND ;
282+ setEmitterOn (false , 0 );
283+ return ;
284+ }
285+
286+ BindingContext ctx = binding .get ();
287+ if (!(ctx .town () instanceof ITownWithResources resourceTown )
288+ || !ctx .warehouse ().isBuildingWorkable ()) {
289+ connectionStatus = STATUS_UNAVAILABLE ;
290+ setEmitterOn (false , 0 );
236291 return ;
237292 }
293+
294+ TeamTownResourceHolder holder = ((TeamTownResourceActionExecutorHandler ) resourceTown .getActionExecutorHandler ()).resourceHolder ;
295+ this .watcher = holder .createWatcher (this ); // 会回调 updateWatcher
296+
297+ connectionStatus = STATUS_WORKING ;
298+ refreshState (); // 主动拉取一次当前库存
299+ }
300+
301+ // ---------- 状态刷新(仅用于配置变更或主动查询) ----------
302+
303+ private void refreshState () {
304+ if (level == null || level .isClientSide ) return ;
305+
238306 Optional <BindingContext > binding = resolveBinding (true );
239307 if (binding .isEmpty ()) {
240308 connectionStatus = STATUS_UNBOUND ;
241309 setEmitterOn (false , 0 );
242310 return ;
243311 }
244312
245- BindingContext context = binding .get ();
246- if (!(context .town () instanceof ITownWithResources resourceTown )
247- || !context .warehouse ().isBuildingWorkable ()) {
313+ BindingContext ctx = binding .get ();
314+ if (!(ctx .town () instanceof ITownWithResources resourceTown )
315+ || !ctx .warehouse ().isBuildingWorkable ()) {
248316 connectionStatus = STATUS_UNAVAILABLE ;
249317 setEmitterOn (false , 0 );
250318 return ;
@@ -257,8 +325,7 @@ private void refreshState() {
257325 }
258326
259327 long stock = (long ) TownResourceActions .get (resourceTown .getActionExecutorHandler (), filter .toStack (1 ));
260- // 与 AE2 发信器一致的判定:HIGH_SIGNAL 在存量 >= 阈值时开启,LOW_SIGNAL 在存量 < 阈值时开启。
261- boolean on = mode == WarehouseRedstoneMode .LOW_SIGNAL ? stock < threshold : stock >= threshold ;
328+ boolean on = (mode == WarehouseRedstoneMode .LOW_SIGNAL ) == (stock < threshold );
262329 setEmitterOn (on , stock );
263330 }
264331
@@ -276,29 +343,42 @@ private void setEmitterOn(boolean on, long stock) {
276343 }
277344 }
278345
279- @ Override
280- public void tick () {
281- if (level != null && !level .isClientSide ) {
282- refreshWorker .tick ();
283- }
284- }
346+ // ---------- 生命周期 ----------
285347
286348 @ Override
287349 public void onLoad () {
288350 super .onLoad ();
289351 if (level != null && !level .isClientSide ) {
290- refreshWorker .enqueue ();
352+ // 方块加载时(无论是首次放置还是 chunk 重载),重新连接 watcher
353+ // 城镇资源常驻,Watcher 机制会自动同步最新库存
354+ refreshWatcherAndState ();
291355 }
292356 }
293357
294358 @ Override
295359 public void onRemoved () {
296360 if (level != null && !level .isClientSide ) {
297- resolveBinding (false ).ifPresent (context -> context .warehouse ().removeEmitter (worldPosition ));
361+ // 只有方块真正被破坏(不再是发信器)才清理 watcher 和绑定
362+ if (!(level .getBlockState (worldPosition ).getBlock () instanceof WarehouseLevelEmitterBlock )) {
363+ // 释放 watcher(自动从索引清理)
364+ if (watcher != null ) {
365+ watcher .reset ();
366+ watcher = null ;
367+ }
368+ // 从仓库注销
369+ resolveBinding (false ).ifPresent (context -> context .warehouse ().removeEmitter (worldPosition ));
370+ // 清空绑定信息
371+ townProvider = null ;
372+ warehousePos = null ;
373+ connectionStatus = STATUS_UNBOUND ;
374+ }
298375 }
299376 super .onRemoved ();
300377 }
301378
379+
380+ // ---------- 序列化 ----------
381+
302382 @ Override
303383 public void readCustomNBT (CompoundTag nbt , boolean descPacket ) {
304384 filter = null ;
@@ -321,7 +401,6 @@ public void readCustomNBT(CompoundTag nbt, boolean descPacket) {
321401 ITownProviderSerializable <? extends ITown > rawProvider =
322402 ITownProviderSerializable .fromNBT (nbt .getCompound ("townProvider" ));
323403 if (rawProvider != null && ITownWithBuildings .class .isAssignableFrom (rawProvider .getTownType ())) {
324- // The runtime type check above guarantees this provider supplies a town with buildings.
325404 townProvider = castTownProvider (rawProvider );
326405 warehousePos = BlockPos .of (nbt .getLong ("warehousePos" ));
327406 }
0 commit comments