基础功能

This commit is contained in:
GaLicn 2025-12-11 20:59:51 +08:00
parent 0b4443773d
commit 044a0c39f7
8 changed files with 172 additions and 14 deletions

View File

@ -105,24 +105,17 @@ public class LabelNetworkRegistry extends SavedData {
}
/**
* 注销端点若网络无端点则清理
* 注销端点不再自动删除网络网络的移除需显式调用 removeNetwork
*/
public synchronized void unregister(IWirelessEndpoint endpoint) {
ServerLevel level = endpoint.getServerLevel();
if (level == null) return;
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
BlockPos pos = endpoint.getBlockPos();
Iterator<Map.Entry<Key, LabelNetwork>> it = networks.entrySet().iterator();
while (it.hasNext()) {
var entry = it.next();
LabelNetwork net = entry.getValue();
for (LabelNetwork net : networks.values()) {
net.endpoints.removeIf(ref -> ref.matches(dimKey, pos));
if (net.endpoints.isEmpty()) {
net.destroyVirtualNode();
it.remove();
setDirty();
}
}
setDirty();
}
public synchronized LabelNetwork getNetwork(ServerLevel level, String rawLabel, @Nullable UUID placerId) {
@ -134,6 +127,24 @@ public class LabelNetworkRegistry extends SavedData {
return networks.get(key);
}
/**
* 显式删除一个网络销毁虚拟节点仅在 UI 删除 时调用
*/
public synchronized boolean removeNetwork(ServerLevel level, String rawLabel, @Nullable UUID placerId) {
String label = normalizeLabel(rawLabel);
if (label == null) return false;
UUID owner = placerId == null ? WirelessMasterRegistry.PUBLIC_NETWORK_UUID : WirelessTeamUtil.getNetworkOwnerUUID(level, placerId);
ResourceKey<Level> dimKey = ModConfig.INSTANCE.wirelessCrossDimEnable ? null : level.dimension();
Key key = new Key(dimKey, label, owner);
LabelNetwork net = networks.remove(key);
if (net != null) {
net.destroyVirtualNode();
setDirty();
return true;
}
return false;
}
/**
* 获取当前玩家所属网络列表按标签排序
*/
@ -147,7 +158,7 @@ public class LabelNetworkRegistry extends SavedData {
if (!Objects.equals(key.dim(), dimKey)) continue;
list.add(new LabelNetworkSnapshot(key.label(), entry.getValue().channel()));
}
list.sort(Comparator.comparing(LabelNetworkSnapshot::label));
list.sort(Comparator.comparingLong(LabelNetworkSnapshot::channel));
return list;
}
@ -190,7 +201,12 @@ public class LabelNetworkRegistry extends SavedData {
}
private long allocateChannel() {
return nextChannel++;
// 全局递增不复用历史频道 1_000_000
if (nextChannel < CHANNEL_START) {
nextChannel = CHANNEL_START;
}
long ch = nextChannel++;
return ch;
}
/* 内部类型 */

View File

@ -54,6 +54,7 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
private final List<LabelEntry> filtered = new ArrayList<>();
private int scrollOffset = 0;
private int selectedIndex = -1;
private String lastSelectedLabel = "";
private String currentLabel = "";
private long currentChannel = 0L;
@ -153,6 +154,7 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
int idx = scrollOffset + row;
if (idx >= 0 && idx < filtered.size()) {
selectedIndex = idx;
lastSelectedLabel = filtered.get(idx).label();
}
return true;
}
@ -245,6 +247,7 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
}
private void applyFilter() {
String prevSelected = lastSelectedLabel;
String q = searchBox.getValue() == null ? "" : searchBox.getValue().trim().toLowerCase();
filtered.clear();
if (q.isEmpty()) {
@ -257,7 +260,16 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
}
}
scrollOffset = 0;
selectedIndex = filtered.isEmpty() ? -1 : Math.min(selectedIndex, filtered.size() - 1);
selectedIndex = -1;
if (prevSelected != null && !prevSelected.isEmpty()) {
for (int i = 0; i < filtered.size(); i++) {
if (filtered.get(i).label().equals(prevSelected)) {
selectedIndex = i;
ensureSelectionVisible();
break;
}
}
}
}
private void requestList() {
@ -267,6 +279,8 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
private void sendSet(String label) {
if (label == null) label = "";
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, label, LabelNetworkActionC2SPacket.Action.SET));
this.lastSelectedLabel = label;
this.searchBox.setValue("");
requestList();
}
@ -277,11 +291,13 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
}
if (label == null) label = "";
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, label, LabelNetworkActionC2SPacket.Action.DELETE));
this.lastSelectedLabel = "";
requestList();
}
private void sendDisconnect() {
ModNetwork.CHANNEL.sendToServer(new LabelNetworkActionC2SPacket(bePos, "", LabelNetworkActionC2SPacket.Action.DISCONNECT));
this.lastSelectedLabel = "";
requestList();
}
@ -293,12 +309,20 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
}
public void updateList(List<LabelNetworkRegistry.LabelNetworkSnapshot> list, String currentLabel, long currentChannel) {
String prevSelected = getSelectedLabel();
this.entries.clear();
for (LabelNetworkRegistry.LabelNetworkSnapshot s : list) {
this.entries.add(new LabelEntry(s.label(), s.channel()));
}
this.currentLabel = currentLabel == null ? "" : currentLabel;
this.currentChannel = currentChannel;
if (prevSelected != null && !prevSelected.isEmpty()) {
this.lastSelectedLabel = prevSelected;
} else if (this.currentLabel != null && !this.currentLabel.isEmpty()) {
this.lastSelectedLabel = this.currentLabel;
} else {
this.lastSelectedLabel = "";
}
applyFilter();
}
@ -329,4 +353,15 @@ public class LabeledWirelessTransceiverScreen extends AbstractContainerScreen<La
int ty = y + (BTN_H - this.font.lineHeight) / 2 + 1;
gfx.drawString(this.font, s, tx, ty, 0xFFFFFF, false);
}
private void ensureSelectionVisible() {
if (selectedIndex < 0) return;
int maxOffset = Math.max(0, filtered.size() - VISIBLE_ROWS);
int targetRow = selectedIndex;
if (targetRow < scrollOffset) {
scrollOffset = targetRow;
} else if (targetRow >= scrollOffset + VISIBLE_ROWS) {
scrollOffset = Math.min(maxOffset, targetRow - VISIBLE_ROWS + 1);
}
}
}

View File

@ -0,0 +1,45 @@
package com.extendedae_plus.integration.jade;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import snownee.jade.api.BlockAccessor;
import snownee.jade.api.IBlockComponentProvider;
import snownee.jade.api.ITooltip;
import snownee.jade.api.config.IPluginConfig;
public enum LabeledWirelessTransceiverComponents implements IBlockComponentProvider {
LABEL_AND_CHANNEL("labeled_wireless_component") {
@Override
protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, net.minecraft.nbt.CompoundTag data) {
String label = data.contains("label") ? data.getString("label") : "";
long channel = data.contains("channel") ? data.getLong("channel") : 0L;
tooltip.add(Component.translatable("extendedae_plus.jade.label", label.isEmpty() ? "-" : label));
tooltip.add(Component.translatable("extendedae_plus.jade.frequency", channel));
if (data.contains("networkUsable")) {
boolean online = data.getBoolean("networkUsable");
tooltip.add(Component.translatable(online ? "extendedae_plus.jade.online" : "extendedae_plus.jade.offline"));
}
}
};
private final ResourceLocation uid;
LabeledWirelessTransceiverComponents(String name) {
this.uid = new ResourceLocation("extendedae_plus", name);
}
@Override
public ResourceLocation getUid() {
return uid;
}
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
if (accessor.getServerData() != null) {
add(accessor, tooltip, config, accessor.getServerData());
}
}
protected abstract void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, net.minecraft.nbt.CompoundTag data);
}

View File

@ -0,0 +1,46 @@
package com.extendedae_plus.integration.jade;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlockEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import snownee.jade.api.BlockAccessor;
import snownee.jade.api.IServerDataProvider;
/**
* 标签无线收发器服务端数据同步
* 仅包含标签名频道网络在线状态
*/
public enum LabeledWirelessTransceiverProvider implements IServerDataProvider<BlockAccessor> {
INSTANCE;
private static final ResourceLocation UID = new ResourceLocation("extendedae_plus", "labeled_wireless_info");
@Override
public ResourceLocation getUid() {
return UID;
}
@Override
public void appendServerData(CompoundTag data, BlockAccessor accessor) {
if (!(accessor.getBlockEntity() instanceof LabeledWirelessTransceiverBlockEntity be)) return;
String label = be.getLabelForDisplay();
if (label != null) {
data.putString("label", label);
}
data.putLong("channel", be.getFrequency());
IGridNode node = be.getGridNode();
IGrid grid = node == null ? null : node.getGrid();
boolean networkUsable = false;
if (grid != null) {
try {
networkUsable = grid.getEnergyService().isNetworkPowered();
} catch (Throwable ignored) {
networkUsable = false;
}
}
data.putBoolean("networkUsable", networkUsable);
}
}

View File

@ -2,6 +2,8 @@ package com.extendedae_plus.integration.jade;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlock;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlock;
import com.extendedae_plus.content.wireless.LabeledWirelessTransceiverBlockEntity;
import snownee.jade.api.IWailaClientRegistration;
import snownee.jade.api.IWailaCommonRegistration;
import snownee.jade.api.IWailaPlugin;
@ -14,6 +16,7 @@ public class WirelessTransceiverJadePlugin implements IWailaPlugin {
public void register(IWailaCommonRegistration registration) {
// 注册服务端数据提供者用于同步数据
registration.registerBlockDataProvider(WirelessTransceiverProvider.INSTANCE, WirelessTransceiverBlockEntity.class);
registration.registerBlockDataProvider(com.extendedae_plus.integration.jade.LabeledWirelessTransceiverProvider.INSTANCE, LabeledWirelessTransceiverBlockEntity.class);
}
@Override
@ -22,5 +25,6 @@ public class WirelessTransceiverJadePlugin implements IWailaPlugin {
for (var component : WirelessTransceiverJadePluginComponents.values()) {
registration.registerBlockComponent(component, WirelessTransceiverBlock.class);
}
registration.registerBlockComponent(com.extendedae_plus.integration.jade.LabeledWirelessTransceiverComponents.LABEL_AND_CHANNEL, LabeledWirelessTransceiverBlock.class);
}
}

View File

@ -52,7 +52,15 @@ public class LabelNetworkActionC2SPacket {
switch (packet.action) {
case SET -> te.applyLabel(packet.label);
case DELETE, DISCONNECT -> te.clearLabel();
case DELETE -> {
String target = packet.label == null || packet.label.isEmpty() ? te.getLabelForDisplay() : packet.label;
if (target != null && !target.isEmpty()) {
com.extendedae_plus.ae.wireless.LabelNetworkRegistry.get(level)
.removeNetwork(level, target, te.getPlacerId());
}
te.clearLabel();
}
case DISCONNECT -> te.clearLabel();
}
});
ctx.get().setPacketHandled(true);

View File

@ -215,10 +215,12 @@
"extendedae_plus.jade.unlocked": "Unlocked",
"extendedae_plus.jade.online": "Device Online",
"extendedae_plus.jade.offline": "Device Offline",
"extendedae_plus.jade.label": "Label: %s",
"extendedae_plus.jade.channels": "Channels: %s",
"extendedae_plus.jade.channels_of": "Channels: %s/%s",
"extendedae_plus.jade.owner": "Owner: %s",
"extendedae_plus.jade.owner.public": "Owner: Public",
"config.jade.plugin_extendedae_plus.labeled_wireless_component": "Labeled Wireless Info",
"extendedae_plus.command.server_side_only": "This command must be run on server side",
"extendedae_plus.command.storage_manager_not_initialized": "InfinityStorageManager is not initialized",

View File

@ -214,10 +214,12 @@
"extendedae_plus.jade.unlocked": "未锁定",
"extendedae_plus.jade.online": "设备在线",
"extendedae_plus.jade.offline": "设备离线",
"extendedae_plus.jade.label": "标签: %s",
"extendedae_plus.jade.channels": "频道: %s",
"extendedae_plus.jade.channels_of": "频道: %s/%s",
"extendedae_plus.jade.owner": "所有者: %s",
"extendedae_plus.jade.owner.public": "所有者: 公共",
"config.jade.plugin_extendedae_plus.labeled_wireless_component": "标签无线收发器信息",
"extendedae_plus.command.server_side_only": "此命令必须在服务器端执行",
"extendedae_plus.command.storage_manager_not_initialized": "InfinityStorageManager未初始化",