Skip to content

Commit c11a7bd

Browse files
committed
feat: support fabric 1.17.1
1 parent eb15e30 commit c11a7bd

21 files changed

Lines changed: 826 additions & 1 deletion

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package net.azisaba.interchatmod.common.util;
2+
3+
import org.jetbrains.annotations.Contract;
4+
import org.jetbrains.annotations.NotNull;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
import java.nio.charset.Charset;
9+
10+
public class ByteStreams {
11+
public static byte @NotNull [] readFully(@NotNull InputStream is) throws IOException {
12+
byte[] bytes = new byte[is.available()];
13+
if (bytes.length > 0 && is.read(bytes) == -1) {
14+
throw new RuntimeException("read nothing");
15+
}
16+
return bytes;
17+
}
18+
19+
@Contract("_, _ -> new")
20+
public static @NotNull String readString(@NotNull InputStream is, Charset charset) throws IOException {
21+
return new String(readFully(is), charset);
22+
}
23+
}

fabric-1.17/build.gradle.kts

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
plugins {
2+
id("fabric-loom") version "1.0-SNAPSHOT"
3+
}
4+
5+
java.toolchain.languageVersion.set(JavaLanguageVersion.of(16))
6+
7+
val minecraftVersion = "1.17.1"
8+
val yarnMappings = "1.17.1+build.65"
9+
val loaderVersion = "0.14.24"
10+
val fabricVersion = "0.46.1+1.17"
11+
val archivesBaseName = "InterChatMod-${project.name}"
12+
val adventureVersion by project.properties
13+
14+
repositories {
15+
// Add repositories to retrieve artifacts from in here.
16+
// You should only use this when depending on other mods because
17+
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
18+
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
19+
// for more information about repositories.
20+
maven { url = uri("https://maven.terraformersmc.com/releases/") }
21+
}
22+
23+
dependencies {
24+
// To change the versions see the gradle.properties file
25+
minecraft("com.mojang:minecraft:$minecraftVersion")
26+
mappings("net.fabricmc:yarn:$yarnMappings:v2")
27+
modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
28+
29+
// Fabric API. This is technically optional, but you probably want it anyway.
30+
modImplementation("net.fabricmc.fabric-api:fabric-api:$fabricVersion")
31+
32+
modImplementation("com.terraformersmc:modmenu:2.0.17")
33+
34+
// Uncomment the following line to enable the deprecated Fabric API modules.
35+
// These are included in the Fabric API production distribution and allow you to update your mod to the latest modules at a later more convenient time.
36+
37+
// modImplementation "net.fabricmc.fabric-api:fabric-api-deprecated:${project.fabric_version}"
38+
implementation(project(":common"))
39+
include(project(":common"))
40+
include("net.kyori:adventure-api:$adventureVersion")
41+
include("net.kyori:adventure-key:$adventureVersion")
42+
include("net.kyori:examination-api:1.3.0")
43+
include("net.kyori:examination-string:1.3.0")
44+
include("net.kyori:adventure-text-serializer-legacy:$adventureVersion")
45+
include("net.kyori:adventure-text-serializer-json:$adventureVersion")
46+
include("net.kyori:adventure-text-serializer-gson:$adventureVersion")
47+
include("org.java-websocket:Java-WebSocket:1.5.4")
48+
}
49+
50+
tasks {
51+
processResources {
52+
inputs.property("version", project.version)
53+
54+
filesMatching("fabric.mod.json") {
55+
expand(mapOf("version" to project.version))
56+
}
57+
}
58+
59+
compileJava {
60+
options.encoding = "UTF-8"
61+
}
62+
63+
remapJar {
64+
archiveFileName.set("$archivesBaseName-${project.version}.jar")
65+
from("LICENSE") {
66+
rename { "${it}_${archivesBaseName}"}
67+
}
68+
}
69+
70+
remapSourcesJar {
71+
archiveFileName.set("$archivesBaseName-${project.version}-sources.jar")
72+
from("LICENSE") {
73+
rename { "${it}_${archivesBaseName}"}
74+
}
75+
}
76+
77+
shadowJar {
78+
archiveBaseName.set("$archivesBaseName-DO-NOT-USE")
79+
}
80+
}
81+
82+
java {
83+
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
84+
// if it is present.
85+
// If you remove this line, sources will not be generated.
86+
withSourcesJar()
87+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package net.azisaba.interchatmod.fabric;
2+
3+
import com.mojang.brigadier.arguments.StringArgumentType;
4+
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
5+
import net.azisaba.interchatmod.fabric.model.Guild;
6+
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
7+
import net.fabricmc.fabric.api.client.command.v1.FabricClientCommandSource;
8+
import net.minecraft.command.CommandSource;
9+
import net.minecraft.text.Text;
10+
11+
public class Commands {
12+
public static LiteralArgumentBuilder<FabricClientCommandSource> builderGS() {
13+
return ClientCommandManager.literal("cgs")
14+
.then(ClientCommandManager.argument("guild", StringArgumentType.word())
15+
.suggests((ctx, builder) -> CommandSource.suggestMatching(Mod.GUILDS.stream().map(Guild::name), builder))
16+
.executes(ctx -> executeFocus(ctx.getSource(), StringArgumentType.getString(ctx, "guild")))
17+
.then(ClientCommandManager.argument("message", StringArgumentType.greedyString())
18+
.executes(ctx -> executeChat(ctx.getSource(), StringArgumentType.getString(ctx, "guild"), StringArgumentType.getString(ctx, "message")))
19+
)
20+
);
21+
}
22+
23+
public static LiteralArgumentBuilder<FabricClientCommandSource> builderG() {
24+
return ClientCommandManager.literal("cg")
25+
.then(ClientCommandManager.argument("message", StringArgumentType.greedyString())
26+
.executes(ctx -> executeChat(ctx.getSource(), null, StringArgumentType.getString(ctx, "message")))
27+
);
28+
}
29+
30+
public static LiteralArgumentBuilder<FabricClientCommandSource> builderReconnectInterChat() {
31+
return ClientCommandManager.literal("reconnectinterchat")
32+
.executes(ctx -> {
33+
Mod.reconnect();
34+
return 0;
35+
})
36+
.then(ClientCommandManager.argument("apikey", StringArgumentType.greedyString())
37+
.executes(ctx -> {
38+
ModConfig.apiKey = StringArgumentType.getString(ctx, "apikey");
39+
Mod.reconnect();
40+
return 0;
41+
})
42+
);
43+
}
44+
45+
public static LiteralArgumentBuilder<FabricClientCommandSource> builderGuild() {
46+
return ClientCommandManager.literal("cguild")
47+
.then(ClientCommandManager.literal("invite")
48+
.then(ClientCommandManager.argument("player", StringArgumentType.word())
49+
.executes(ctx -> {
50+
Mod.client.invite(StringArgumentType.getString(ctx, "player"));
51+
return 0;
52+
})
53+
)
54+
)
55+
.then(ClientCommandManager.literal("accept")
56+
.then(ClientCommandManager.argument("guild", StringArgumentType.word())
57+
.executes(ctx -> {
58+
Mod.client.respondInvite(StringArgumentType.getString(ctx, "guild"), true);
59+
return 0;
60+
})
61+
)
62+
)
63+
.then(ClientCommandManager.literal("reject")
64+
.then(ClientCommandManager.argument("guild", StringArgumentType.word())
65+
.executes(ctx -> {
66+
Mod.client.respondInvite(StringArgumentType.getString(ctx, "guild"), false);
67+
return 0;
68+
})
69+
)
70+
)
71+
.then(ClientCommandManager.literal("nick")
72+
.executes(ctx -> {
73+
Mod.client.nick(null);
74+
return 0;
75+
})
76+
.then(ClientCommandManager.argument("nickname", StringArgumentType.greedyString())
77+
.executes(ctx -> {
78+
Mod.client.nick(StringArgumentType.getString(ctx, "nickname"));
79+
return 0;
80+
})
81+
)
82+
);
83+
}
84+
85+
private static int executeFocus(FabricClientCommandSource source, String guildName) {
86+
Guild guild = Mod.GUILDS.stream().filter(g -> g.name().equalsIgnoreCase(guildName)).findAny().orElse(null);
87+
if (guild == null) {
88+
source.sendError(Text.of("そんなぎるどないよ " + guildName));
89+
return 0;
90+
}
91+
Mod.client.selectGuild(guild.id());
92+
source.sendFeedback(Text.of(guild.name() + " にちゃっとするようにしたよ(/cg <めっせーじ>でできるよ)"));
93+
return 1;
94+
}
95+
96+
private static int executeChat(FabricClientCommandSource source, String guildName, String message) {
97+
if (guildName != null) {
98+
Guild guild = Mod.GUILDS.stream().filter(g -> g.name().equalsIgnoreCase(guildName)).findAny().orElse(null);
99+
if (guild == null) {
100+
source.sendError(Text.of("そんなぎるどないよ " + guildName));
101+
return 0;
102+
}
103+
Mod.client.sendMessageToGuild(guild.id(), message);
104+
} else {
105+
Mod.client.sendMessageToGuild(null, message);
106+
}
107+
return 1;
108+
}
109+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package net.azisaba.interchatmod.fabric;
2+
3+
import com.terraformersmc.modmenu.api.ConfigScreenFactory;
4+
import com.terraformersmc.modmenu.api.ModMenuApi;
5+
6+
public class InterChatModMenuApiImpl implements ModMenuApi {
7+
@Override
8+
public ConfigScreenFactory<?> getModConfigScreenFactory() {
9+
return ModConfigScreen::new;
10+
}
11+
}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
package net.azisaba.interchatmod.fabric;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonArray;
5+
import com.google.gson.JsonElement;
6+
import com.google.gson.JsonObject;
7+
import com.terraformersmc.modmenu.api.ModMenuApi;
8+
import net.azisaba.interchatmod.common.util.ByteStreams;
9+
import net.azisaba.interchatmod.fabric.model.Guild;
10+
import net.fabricmc.api.ModInitializer;
11+
import net.fabricmc.fabric.api.client.command.v1.ClientCommandManager;
12+
import net.minecraft.client.MinecraftClient;
13+
import net.minecraft.client.network.ServerInfo;
14+
import net.minecraft.server.integrated.IntegratedServer;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
18+
19+
import javax.net.ssl.SSLContext;
20+
import javax.net.ssl.SSLSocketFactory;
21+
import java.net.HttpURLConnection;
22+
import java.net.URI;
23+
import java.net.URL;
24+
import java.nio.charset.StandardCharsets;
25+
import java.util.*;
26+
27+
public class Mod implements ModInitializer, ModMenuApi {
28+
public static final Logger LOGGER = LoggerFactory.getLogger("InterChatMod");
29+
public static final Timer TIMER = new Timer(true);
30+
public static final Set<Guild> GUILDS = Collections.synchronizedSet(new HashSet<>());
31+
public static WebSocketChatClient client;
32+
33+
@Override
34+
public void onInitialize() {
35+
ClientCommandManager.DISPATCHER.register(Commands.builderGS());
36+
ClientCommandManager.DISPATCHER.register(Commands.builderG());
37+
ClientCommandManager.DISPATCHER.register(Commands.builderReconnectInterChat());
38+
ClientCommandManager.DISPATCHER.register(Commands.builderGuild());
39+
40+
ModConfig.load();
41+
42+
TIMER.schedule(new TimerTask() {
43+
@Override
44+
public void run() {
45+
try {
46+
String url = "https://api-ktor.azisaba.net/interchat/guilds/list";
47+
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
48+
connection.addRequestProperty("Authorization", "Bearer " + ModConfig.apiKey);
49+
String response = ByteStreams.readString(connection.getInputStream(), StandardCharsets.UTF_8);
50+
JsonArray arr = new Gson().fromJson(response, JsonArray.class);
51+
Set<Guild> localGuilds = getGuildsFromArray(arr);
52+
GUILDS.clear();
53+
GUILDS.addAll(localGuilds);
54+
} catch (Exception e) {
55+
LOGGER.warn("Failed to fetch guild list", e);
56+
}
57+
}
58+
}, 1000 * 30, 1000 * 30);
59+
60+
reconnect();
61+
}
62+
63+
public static void reconnect() {
64+
try {
65+
if (client != null) {
66+
client.close();
67+
}
68+
LOGGER.info("Attempting to connect to the server");
69+
URI uri = new URI("wss://api-ktor.azisaba.net/interchat/stream?server=dummy");
70+
client = new WebSocketChatClient(uri);
71+
if (uri.getScheme().startsWith("wss")) {
72+
SSLContext sslContext = SSLContext.getInstance("TLS");
73+
sslContext.init(null, null, null);
74+
SSLSocketFactory factory = sslContext.getSocketFactory();
75+
client.setSocketFactory(factory);
76+
}
77+
client.connectBlocking();
78+
} catch (Exception e) {
79+
LOGGER.error("Failed to establish WebSocket session", e);
80+
}
81+
}
82+
83+
@NotNull
84+
private static Set<Guild> getGuildsFromArray(JsonArray arr) {
85+
Set<Guild> localGuilds = new HashSet<>();
86+
for (JsonElement element : arr) {
87+
JsonObject obj = element.getAsJsonObject();
88+
localGuilds.add(
89+
new Guild(
90+
obj.get("id").getAsLong(),
91+
obj.get("name").getAsString(),
92+
obj.get("format").getAsString(),
93+
obj.get("capacity").getAsInt(),
94+
obj.get("open").getAsBoolean(),
95+
obj.get("deleted").getAsBoolean()
96+
)
97+
);
98+
}
99+
return localGuilds;
100+
}
101+
102+
public static boolean isInAzisaba() {
103+
ServerInfo serverInfo = MinecraftClient.getInstance().getCurrentServerEntry();
104+
if (serverInfo == null) return false;
105+
return serverInfo.address.endsWith(".azisaba.net") || serverInfo.address.equals("azisaba.net");
106+
}
107+
108+
public static void trySwitch() {
109+
if (client == null) return;
110+
ServerInfo serverData = MinecraftClient.getInstance().getCurrentServerEntry();
111+
if (serverData != null) {
112+
client.switchServer(serverData.address);
113+
}
114+
IntegratedServer singleServer = MinecraftClient.getInstance().getServer();
115+
if (singleServer != null) {
116+
client.switchServer(singleServer.getSaveProperties().getLevelName());
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)