-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathLANAnnouncer.java
116 lines (96 loc) · 3.96 KB
/
LANAnnouncer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package is.meh.minecraft.lan_announcer;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.MinecraftServer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class LANAnnouncer implements ModInitializer {
public static final Logger LOGGER = LoggerFactory.getLogger("lan-announcer");
private ServerAnnouncer ipv4Announcer;
private ServerAnnouncer ipv6Announcer;
@Override
public void onInitialize() {
if (FabricLoader.getInstance().getEnvironmentType() == net.fabricmc.api.EnvType.SERVER) {
ServerLifecycleEvents.SERVER_STARTED.register(this::onServerStarted);
ServerLifecycleEvents.SERVER_STOPPING.register(this::onServerStopping);
} else {
LOGGER.warn(
"This mod is only intended for dedicated multiplayer" +
"servers, not clients. Clients can start their own built-in" +
"LAN broadcast by using `Open to LAN`"
);
}
}
// Prevent conflicts with the packet payload
private String sanitizeMOTD(String motd) {
return motd.replace("[", "").replace("]", "");
}
private void onServerStarted(MinecraftServer server) {
String motd = "[MOTD]" + sanitizeMOTD(server.getServerMotd()) + "[/MOTD]";
String port = "[AD]" + server.getServerPort() + "[/AD]";
// https://github.com/meh-is/LANAnnouncer/issues/5#issuecomment-1873572736
String uuid = "[LAN_SERVER_ID]" + UUID.randomUUID() + "[/LAN_SERVER_ID]";
byte[] message = (motd + port + uuid).getBytes();
// Initialize and start the IPv4 and IPv6 announcers
ipv4Announcer = new ServerAnnouncer("224.0.2.60", message);
ipv4Announcer.startAnnouncing();
ipv6Announcer = new ServerAnnouncer("ff75:230::60", message);
ipv6Announcer.startAnnouncing();
}
private void onServerStopping(MinecraftServer server) {
ipv4Announcer.stopAnnouncing();
ipv6Announcer.stopAnnouncing();
}
static class ServerAnnouncer {
private final byte[] message;
private DatagramSocket socket;
private ScheduledExecutorService executorService;
private boolean running;
private InetAddress address;
public ServerAnnouncer(String ipAddress, byte[] message) {
try {
socket = new DatagramSocket();
socket.setBroadcast(true);
address = InetAddress.getByName(ipAddress);
} catch (IOException e) {
LOGGER.error("ServerAnnouncer: ", e);
}
this.message = message;
}
public void startAnnouncing() {
running = true;
executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(this::announce, 0, 1500, TimeUnit.MILLISECONDS);
}
private void announce() {
if (!running) {
executorService.shutdown();
return;
}
try {
DatagramPacket packet = new DatagramPacket(message, message.length, address, 4445);
socket.send(packet);
} catch (IOException e) {
LOGGER.error("Error in startAnnouncing", e);
}
}
public void stopAnnouncing() {
running = false;
if (socket != null && !socket.isClosed()) {
socket.close();
}
if (executorService != null && !executorService.isShutdown()) {
executorService.shutdownNow();
}
}
}
}