Skip to content

Commit

Permalink
add HTTPAPI
Browse files Browse the repository at this point in the history
  • Loading branch information
iceBear67 committed Jan 6, 2025
1 parent dddabe3 commit ee1c92e
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 4 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ dependencies {
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
shadow('com.maxmind.geoip2:geoip2:4.2.0')
implementation("io.javalin:javalin:6.4.0")
implementation "io.javalin:javalin:6.4.0"
implementation 'com.github.houbb:pinyin:0.4.0'
shadow('com.google.inject:guice:7.0.0') {
exclude group: 'com.google.guava', module: 'guava'
exclude group: 'com.google.guava', module: 'listenablefuture'
Expand Down
96 changes: 96 additions & 0 deletions src/main/java/io/ib67/sfcraft/module/supervisor/WebModule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.ib67.sfcraft.module.supervisor;

import com.google.inject.Inject;
import io.ib67.sfcraft.ServerModule;
import io.ib67.sfcraft.config.SFConfig;
import io.ib67.sfcraft.module.SignatureService;
import io.ib67.sfcraft.util.Helper;
import io.ib67.sfcraft.util.LitematicConverter;
import io.javalin.Javalin;
import io.javalin.http.Context;
import io.javalin.http.UploadedFile;
import io.netty.buffer.Unpooled;
import lombok.SneakyThrows;
import lombok.extern.log4j.Log4j2;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.NbtSizeTracker;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.regex.Pattern;

@Log4j2
public class WebModule extends ServerModule {
private static final int PERMISSION_UPLOAD_SCHEMATICS = 2;
private static final Path SCHEMATIC_DIR = Path.of("config/worldedit/schematics/");
@Inject
SFConfig config;
@Inject
SignatureService signatureService;

@Override
@SneakyThrows
public void onInitialize() {
Files.createDirectories(SCHEMATIC_DIR);
Thread.ofVirtual().name("SFCraft API Web").start(() -> {
Javalin.create(cfg -> cfg.useVirtualThreads = true)
.post("/api/schematics", this::uploadSchematic)
.start(config.httpPort);
});
}

private void uploadSchematic(@NotNull Context context) {
if (context.contentLength() > config.maxSchematicSize) {
context.result("Content is too large!");
return;
}
var signRaw = Base64.getUrlDecoder().decode(context.pathParam("sign"));
var verifiedSign = signatureService.readSignature(Unpooled.wrappedBuffer(signRaw));
if ((verifiedSign.permission() & PERMISSION_UPLOAD_SCHEMATICS) == 0) {
context.result("Permission denied!");
return;
}
var files = context.uploadedFileMap();
files.forEach((k, v) -> handleUploadSchematic(context, k, v));
}

@SneakyThrows
private void handleUploadSchematic(@NotNull Context context, String fileName, List<UploadedFile> v) {
if (v.size() != 1) {
return;
}
var file = v.getFirst();
if (file.size() > config.maxSchematicSize) {
context.result("Content is too large!");
return;
}
fileName = Helper.cleanFileName(fileName);
if (fileName.endsWith(".schematic")) {
Files.write(SCHEMATIC_DIR.resolve(fileName), file.content().readAllBytes());
file.content().close();
log.info("Saved "+fileName+" as a schematic.");
context.result("Success!");
} else if (fileName.endsWith(".litematic")) {
log.error("Handling new {}", fileName);
var baseFileName = fileName.substring(0, fileName.length() - 10);
new LitematicConverter(
file.content(),
new NbtSizeTracker(config.maxSchematicSize, 16)
).read((name, nbt) -> {
name = Helper.cleanFileName(baseFileName + "-" + name + ".schematic");
try {
NbtIo.writeCompressed(nbt, SCHEMATIC_DIR.resolve(name));
log.info("Schematic" + name+" has been saved!");
} catch (IOException e) {
log.error("Error occurred when serializing .schematic from .litematic.", e);
}
});
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/io/ib67/sfcraft/util/Helper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package io.ib67.sfcraft.util;

import com.github.houbb.pinyin.api.impl.Pinyin;
import com.github.houbb.pinyin.constant.enums.PinyinStyleEnum;
import com.github.houbb.pinyin.util.PinyinHelper;
import com.maxmind.geoip2.exception.GeoIp2Exception;
import io.ib67.sfcraft.SFCraft;
import io.ib67.sfcraft.config.SFConfig;
Expand Down Expand Up @@ -29,8 +32,10 @@
import java.nio.file.Path;
import java.util.Optional;
import java.util.UUID;
import java.util.regex.Pattern;

public class Helper {
private static final Pattern ILLEGAL_CHARACTERS = Pattern.compile("[./\\\\#%!@&*]");
public static final char COLOR = '§';

public static boolean canBack(ServerPlayerEntity player) {
Expand All @@ -43,6 +48,15 @@ public static boolean canBack(ServerPlayerEntity player) {
return nearby != null;
}

public static String cleanFileName(String filename) {
var name = PinyinHelper.toPinyin(filename, PinyinStyleEnum.NORMAL,"_");
name = ILLEGAL_CHARACTERS.matcher(name).replaceAll("");
if(name.length() > 32){
name = name.substring(0,32);
}
return name;
}

public static boolean teleportSafely(ServerPlayerEntity player, ServerWorld world, int x, int y, int z, float yaw, float pitch) {
var pos = new BlockPos(x, y, z);
var stand = world.getBlockState(pos);
Expand Down
4 changes: 1 addition & 3 deletions src/main/java/io/ib67/sfcraft/util/LitematicConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ public class LitematicConverter implements AutoCloseable {
public static final String NBT_LITEMATICA_ROOT = "";
private final InputStream input;
private final NbtSizeTracker sizeTracker;
private NbtCompound root;

public LitematicConverter(InputStream input, NbtSizeTracker sizeTracker) {
this.input = input;
Expand All @@ -31,8 +30,7 @@ public LitematicConverter(InputStream input, NbtSizeTracker sizeTracker) {
public void read(
BiConsumer<String, NbtCompound> schematicOutput
) {
if (root != null) throw new IllegalStateException("Litematica file is already read");
root = NbtIo.readCompressed(input, sizeTracker).getCompound(NBT_LITEMATICA_ROOT);
var root = NbtIo.readCompressed(input, sizeTracker).getCompound(NBT_LITEMATICA_ROOT);
var dataVersion = root.getInt("MinecraftDataVersion");
var regionsNbt = root.getCompound("Regions");
var i = 0;
Expand Down

0 comments on commit ee1c92e

Please sign in to comment.