优化频道卡连接逻辑

This commit is contained in:
GaLi 2026-03-26 17:45:49 +08:00
parent 9d126e65a0
commit b86ac6867a
8 changed files with 209 additions and 128 deletions

View File

@ -9,6 +9,7 @@ import appeng.api.stacks.KeyCounter;
import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.api.upgrades.UpgradeInventories;
import appeng.blockentity.AEBaseBlockEntity;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import appeng.me.cluster.implementations.CraftingCPUCluster;
@ -20,11 +21,11 @@ import com.extendedae_plus.api.bridge.PatternProviderLogicUpgradeCompatBridge;
import com.extendedae_plus.compat.PatternProviderLogicVirtualCompatBridge;
import com.extendedae_plus.compat.UpgradeSlotCompat;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.mixin.ae2.accessor.CraftingCpuLogicAccessor;
import com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobAccessor;
import com.extendedae_plus.mixin.appflux.accessor.PatternProviderLogicAppfluxAccessor;
import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.util.wireless.ChannelCardLinkHelper;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@ -102,7 +103,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
@Unique
private void eap$compatOnUpgradesChanged() {
try {
this.host.saveChanges();
this.eap$compatNotifyHostChanged();
this.eap$compatLastChannel = -1;
this.eap$compatLastOwner = null;
this.eap$compatHasInitialized = false;
@ -191,7 +192,11 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
@Override
public void eap$updateWirelessLink() {
if (this.eap$compatLink != null) {
boolean wasConnected = this.eap$compatLink.isConnected();
this.eap$compatLink.updateStatus();
if (wasConnected != this.eap$compatLink.isConnected()) {
this.eap$compatNotifyHostChanged();
}
}
}
@ -239,21 +244,10 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
if (this.host.getBlockEntity() == null || this.host.getBlockEntity().getLevel() == null || this.host.getBlockEntity().getLevel().isClientSide) {
return false;
}
// 未初始化需要继续tick直到初始化完成
if (!this.eap$compatHasInitialized) {
return true;
}
// 有频道卡但链接未连上保持tick
IUpgradeInventory upgrades = this.eap$compatGetEffectiveUpgrades();
if (upgrades != null && this.eap$hasChannelCard(upgrades)) {
if (this.eap$compatLink == null || !this.eap$compatLink.isConnected()) {
return true;
}
}
// 链接存在但未连接保持tick
if (this.eap$compatLink != null && !this.eap$compatLink.isConnected()) {
return true;
}
return ChannelCardLinkHelper.shouldKeepTicking(
this.eap$compatGetEffectiveUpgrades(),
this.eap$compatLink,
this.eap$compatHasInitialized);
} catch (Throwable ignored) {
}
return false;
@ -300,34 +294,20 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
}
}
if (upgrades != null) {
for (ItemStack stack : upgrades) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
owner = ChannelCardItem.getOwnerUUID(stack);
if (owner == null) {
owner = this.eap$getFallbackOwner();
}
found = true;
break;
}
}
var boundChannel = ChannelCardLinkHelper.findBoundChannel(upgrades, this::eap$getFallbackOwner);
if (boundChannel != null) {
channel = boundChannel.channel();
owner = boundChannel.owner();
found = true;
}
if (!found) {
this.eap$compatSyncVirtualCraftingState();
if (this.eap$compatLink != null) {
this.eap$compatLink.setPlacerId(null);
this.eap$compatLink.setFrequency(0L);
this.eap$compatLink.updateStatus();
}
ChannelCardLinkHelper.disconnect(this.eap$compatLink);
this.eap$compatLastChannel = 0L;
this.eap$compatLastOwner = null;
this.eap$compatHasInitialized = true;
try {
this.host.saveChanges();
} catch (Throwable ignored) {
}
this.eap$compatNotifyHostChanged();
// 唤醒节点加速 AE2 感知到连接断开
this.mainNode.ifPresent((grid, node) -> {
try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
@ -348,9 +328,8 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
return;
}
boolean sameOwner = (this.eap$compatLastOwner == null && owner == null)
|| (this.eap$compatLastOwner != null && this.eap$compatLastOwner.equals(owner));
if (this.eap$compatLink != null && this.eap$compatLastChannel == channel && sameOwner) {
if (this.eap$compatLink != null
&& ChannelCardLinkHelper.sameTarget(this.eap$compatLastChannel, this.eap$compatLastOwner, boundChannel)) {
if (this.eap$compatLink.isConnected()) {
this.eap$compatHasInitialized = true;
}
@ -367,10 +346,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
this.eap$compatLink.updateStatus();
this.eap$compatLastChannel = channel; // 记录当前频道
this.eap$compatLastOwner = owner;
try {
this.host.saveChanges();
} catch (Throwable ignored) {
}
this.eap$compatNotifyHostChanged();
this.mainNode.ifPresent((grid, node) -> {
try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
});
@ -399,10 +375,25 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr
return null;
}
@Unique
private void eap$compatNotifyHostChanged() {
try {
this.host.saveChanges();
} catch (Throwable ignored) {
}
try {
if (this.host.getBlockEntity() instanceof AEBaseBlockEntity blockEntity) {
blockEntity.markForUpdate();
}
} catch (Throwable ignored) {
}
}
// CompatUpgradeProvider 实现仅在未安装 appflux 时由我们提供升级槽
@Unique
private boolean eap$hasChannelCard(IUpgradeInventory inventory) {
return this.eap$compatInventoryContains(inventory, ModItems.CHANNEL_CARD.get());
return ChannelCardLinkHelper.hasChannelCard(inventory);
}
/**

View File

@ -6,9 +6,7 @@ import appeng.helpers.InterfaceLogicHost;
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
import com.extendedae_plus.ae.wireless.endpoint.InterfaceNodeEndpointImpl;
import com.extendedae_plus.api.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import net.minecraft.world.item.ItemStack;
import com.extendedae_plus.util.wireless.ChannelCardLinkHelper;
import java.util.UUID;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -118,6 +116,11 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
this.eap$hasInitialized = initialized;
}
@Override
public boolean eap$shouldKeepTicking() {
return ChannelCardLinkHelper.shouldKeepTicking(this.getUpgrades(), this.eap$link, this.eap$hasInitialized);
}
@Override
@Unique
public void eap$initializeChannelLink() {
@ -137,28 +140,14 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
}
try {
long channel = 0L;
boolean found = false;
UUID owner = null;
for (ItemStack stack : this.getUpgrades()) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
owner = ChannelCardItem.getOwnerUUID(stack);
if (owner == null) {
owner = this.eap$getFallbackOwner();
}
found = true;
break;
}
}
var boundChannel = ChannelCardLinkHelper.findBoundChannel(this.getUpgrades(), this::eap$getFallbackOwner);
long channel = boundChannel != null ? boundChannel.channel() : 0L;
boolean found = boundChannel != null;
UUID owner = boundChannel != null ? boundChannel.owner() : null;
if (!found) {
// 无频道卡断开并视为初始化完成
if (this.eap$link != null) {
this.eap$link.setPlacerId(null);
this.eap$link.setFrequency(0L);
this.eap$link.updateStatus();
}
ChannelCardLinkHelper.disconnect(this.eap$link);
this.eap$hasInitialized = true;
this.eap$lastChannel = 0L;
this.eap$lastOwner = null;
@ -176,9 +165,8 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
return;
}
boolean sameOwner = (this.eap$lastOwner == null && owner == null)
|| (this.eap$lastOwner != null && this.eap$lastOwner.equals(owner));
if (this.eap$link != null && this.eap$lastChannel == channel && sameOwner) {
if (this.eap$link != null
&& ChannelCardLinkHelper.sameTarget(this.eap$lastChannel, this.eap$lastOwner, boundChannel)) {
this.eap$hasInitialized = this.eap$link.isConnected();
return;
}

View File

@ -1,5 +1,6 @@
package com.extendedae_plus.mixin.ae2.helpers;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.helpers.InterfaceLogic;
import com.extendedae_plus.api.bridge.InterfaceWirelessLinkBridge;
import org.spongepowered.asm.mixin.Final;
@ -32,11 +33,17 @@ public abstract class InterfaceLogicTickerMixin {
}
}
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false, cancellable = true)
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable<appeng.api.networking.ticking.TickRateModulation> cir) {
if (node != null && node.getLevel() != null && node.getLevel().isClientSide) {
return;
}
if (this.this$0 instanceof InterfaceWirelessLinkBridge bridge) {
bridge.eap$updateWirelessLink();
if (bridge.eap$shouldKeepTicking() && cir.getReturnValue() == TickRateModulation.SLEEP) {
cir.setReturnValue(TickRateModulation.SLOWER);
}
}
}
}

View File

@ -6,6 +6,7 @@ import appeng.api.upgrades.UpgradeInventories;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import com.extendedae_plus.api.bridge.PatternProviderLogicAppfluxBridge;
import com.extendedae_plus.api.bridge.PatternProviderLogicUpgradeCompatBridge;
import com.extendedae_plus.compat.UpgradeSlotCompat;
import com.extendedae_plus.mixin.appflux.accessor.PatternProviderLogicAppfluxAccessor;
import com.extendedae_plus.util.ExtendedAELogger;
@ -113,10 +114,10 @@ public abstract class PatternProviderLogicUpgradesMixin implements PatternProvid
this.host.saveChanges();
try {
((PatternProviderLogicAppfluxAccessor) (Object) this).eap$invokeAppfluxUpgradesChanged();
return;
} catch (Throwable ignored) {
}
if ((Object) this instanceof com.extendedae_plus.api.bridge.PatternProviderLogicUpgradeCompatBridge bridge) {
if ((Object) this instanceof PatternProviderLogicUpgradeCompatBridge bridge) {
bridge.eap$onCompatUpgradesChangedHook();
}
} catch (Throwable t) {

View File

@ -1,6 +1,7 @@
package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.api.networking.ticking.TickRateModulation;
import com.extendedae_plus.api.bridge.InterfaceWirelessLinkBridge;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -41,8 +42,8 @@ public abstract class PatternProviderLogicTickerMixin {
}
if (this.this$0 instanceof InterfaceWirelessLinkBridge bridge) {
bridge.eap$updateWirelessLink();
if (bridge.eap$shouldKeepTicking()) {
cir.setReturnValue(appeng.api.networking.ticking.TickRateModulation.SLOWER);
if (bridge.eap$shouldKeepTicking() && cir.getReturnValue() == TickRateModulation.SLEEP) {
cir.setReturnValue(TickRateModulation.SLOWER);
}
}
}

View File

@ -1,15 +1,15 @@
package com.extendedae_plus.mixin.ae2.parts.automation;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.parts.automation.IOBusPart;
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
import com.extendedae_plus.api.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.util.wireless.ChannelCardLinkHelper;
import net.minecraft.nbt.CompoundTag;
import java.util.UUID;
import org.spongepowered.asm.mixin.Mixin;
@ -17,6 +17,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* AE2 I/O 总线注入频道卡联动在升级变更时读取频道并更新无线链接
@ -56,6 +57,19 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
}
}
@Inject(method = "tickingRequest", at = @At("TAIL"), cancellable = true)
private void eap$afterTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable<TickRateModulation> cir) {
if (((appeng.parts.AEBasePart) (Object) this).isClientSide()) {
return;
}
this.eap$updateWirelessLink();
if (this.eap$shouldKeepTicking() && cir.getReturnValue() == TickRateModulation.SLEEP) {
cir.setReturnValue(TickRateModulation.SLOWER);
}
}
@Inject(method = "readFromNBT", at = @At("TAIL"))
private void eap$afterReadFromNBT(CompoundTag extra, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) {
// 从NBT加载时重置频道缓存和tick初始化标志
@ -86,6 +100,11 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
this.eap$clientConnected = connected;
}
@Override
public boolean eap$shouldKeepTicking() {
return ChannelCardLinkHelper.shouldKeepTicking(this.getUpgrades(), this.eap$link, this.eap$hasTickInitialized);
}
@Override
@Unique
public void eap$initializeChannelLink() {
@ -96,25 +115,14 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
try {
IUpgradeInventory inv = this.getUpgrades();
long channel = 0L;
boolean found = false;
UUID owner = null;
for (var stack : inv) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
owner = ChannelCardItem.getOwnerUUID(stack);
if (owner == null) {
owner = this.eap$getFallbackOwner();
}
found = true;
break;
}
}
var boundChannel = ChannelCardLinkHelper.findBoundChannel(inv, this::eap$getFallbackOwner);
long channel = boundChannel != null ? boundChannel.channel() : 0L;
boolean found = boundChannel != null;
UUID owner = boundChannel != null ? boundChannel.owner() : null;
// 频道没有变化则跳过
boolean sameOwner = (this.eap$lastOwner == null && owner == null)
|| (this.eap$lastOwner != null && this.eap$lastOwner.equals(owner));
if (this.eap$link != null && this.eap$lastChannel == channel && sameOwner) {
if (this.eap$link != null
&& ChannelCardLinkHelper.sameTarget(this.eap$lastChannel, this.eap$lastOwner, boundChannel)) {
return;
}
this.eap$lastChannel = channel;
@ -122,13 +130,9 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
if (!found) {
// 无频道卡则断开
if (this.eap$link != null) {
this.eap$link.setPlacerId(null);
this.eap$link.setFrequency(0L);
this.eap$link.updateStatus();
// 立即通知客户端状态变化断开连接无需延迟
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
}
ChannelCardLinkHelper.disconnect(this.eap$link);
// 立即通知客户端状态变化断开连接无需延迟
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
this.eap$lastChannel = 0L;
this.eap$lastOwner = null;
return;

View File

@ -2,15 +2,15 @@ package com.extendedae_plus.mixin.ae2.parts.storagebus;
import appeng.api.networking.IGridNodeListener;
import appeng.api.networking.security.IActionHost;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.parts.storagebus.StorageBusPart;
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl;
import com.extendedae_plus.api.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.util.wireless.ChannelCardLinkHelper;
import net.minecraft.nbt.CompoundTag;
import java.util.UUID;
import org.spongepowered.asm.mixin.Mixin;
@ -18,6 +18,7 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/**
* AE2 的存储总线注入频道卡联动在升级变更时读取频道并更新无线链接
@ -63,6 +64,19 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
}
}
@Inject(method = "tickingRequest", at = @At("TAIL"), cancellable = true)
private void eap$afterTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable<TickRateModulation> cir) {
if (((appeng.parts.AEBasePart) (Object) this).isClientSide()) {
return;
}
this.eap$updateWirelessLink();
if (this.eap$shouldKeepTicking() && cir.getReturnValue() == TickRateModulation.SLEEP) {
cir.setReturnValue(TickRateModulation.SLOWER);
}
}
@Override
public void eap$updateWirelessLink() {
if (this.eap$link != null) {
@ -84,6 +98,11 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
this.eap$clientConnected = connected;
}
@Override
public boolean eap$shouldKeepTicking() {
return ChannelCardLinkHelper.shouldKeepTicking(this.getUpgrades(), this.eap$link, true);
}
@Override
@Unique
public void eap$initializeChannelLink() {
@ -94,38 +113,23 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
try {
IUpgradeInventory inv = this.getUpgrades();
long channel = 0L;
boolean found = false;
UUID owner = null;
for (var stack : inv) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
owner = ChannelCardItem.getOwnerUUID(stack);
if (owner == null) {
owner = this.eap$getFallbackOwner();
}
found = true;
break;
}
}
var boundChannel = ChannelCardLinkHelper.findBoundChannel(inv, this::eap$getFallbackOwner);
long channel = boundChannel != null ? boundChannel.channel() : 0L;
boolean found = boundChannel != null;
UUID owner = boundChannel != null ? boundChannel.owner() : null;
// 频道没有变化则跳过
boolean sameOwner = (this.eap$lastOwner == null && owner == null)
|| (this.eap$lastOwner != null && this.eap$lastOwner.equals(owner));
if (this.eap$link != null && this.eap$lastChannel == channel && sameOwner) {
if (this.eap$link != null
&& ChannelCardLinkHelper.sameTarget(this.eap$lastChannel, this.eap$lastOwner, boundChannel)) {
return;
}
this.eap$lastChannel = channel;
if (!found) {
if (this.eap$link != null) {
this.eap$link.setPlacerId(null);
this.eap$link.setFrequency(0L);
this.eap$link.updateStatus();
// 通知客户端状态变化
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
}
ChannelCardLinkHelper.disconnect(this.eap$link);
// 通知客户端状态变化
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
this.eap$lastChannel = 0L;
this.eap$lastOwner = null;
return;

View File

@ -0,0 +1,85 @@
package com.extendedae_plus.util.wireless;
import appeng.api.upgrades.IUpgradeInventory;
import com.extendedae_plus.ae.wireless.WirelessSlaveLink;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.items.materials.ChannelCardItem;
import java.util.Objects;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
/**
* 统一处理频道卡升级槽扫描与无线从端保活判定避免各宿主实现漂移
*/
public final class ChannelCardLinkHelper {
private ChannelCardLinkHelper() {
}
public record BoundChannel(long channel, @Nullable UUID owner) {
}
@Nullable
public static BoundChannel findBoundChannel(@Nullable IUpgradeInventory upgrades, Supplier<UUID> fallbackOwner) {
if (upgrades == null) {
return null;
}
for (ItemStack stack : upgrades) {
if (stack.isEmpty() || stack.getItem() != ModItems.CHANNEL_CARD.get()) {
continue;
}
UUID owner = ChannelCardItem.getOwnerUUID(stack);
if (owner == null) {
owner = fallbackOwner.get();
}
return new BoundChannel(ChannelCardItem.getChannel(stack), owner);
}
return null;
}
public static boolean hasChannelCard(@Nullable IUpgradeInventory upgrades) {
if (upgrades == null) {
return false;
}
for (ItemStack stack : upgrades) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
return true;
}
}
return false;
}
public static boolean sameTarget(long lastChannel, @Nullable UUID lastOwner, @Nullable BoundChannel boundChannel) {
return boundChannel != null
&& lastChannel == boundChannel.channel()
&& Objects.equals(lastOwner, boundChannel.owner());
}
public static boolean hasActiveLink(@Nullable WirelessSlaveLink link) {
return link != null && (link.getFrequency() != 0L || link.isConnected());
}
public static boolean shouldKeepTicking(@Nullable IUpgradeInventory upgrades,
@Nullable WirelessSlaveLink link,
boolean initialized) {
return !initialized || hasChannelCard(upgrades) || hasActiveLink(link);
}
public static void disconnect(@Nullable WirelessSlaveLink link) {
if (link == null) {
return;
}
link.setPlacerId(null);
link.setFrequency(0L);
link.updateStatus();
}
}