mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-01-30 14:55:07 +01:00
Schematic Caching
Currently schematics are always reuploaded even if they already exist on the server, these changes make it so if they already exist on the server, a schematic pointing towards that is made instead of requiring a full reupload.
This commit is contained in:
parent
aa15182005
commit
5d2c8f281c
6 changed files with 113 additions and 24 deletions
|
@ -2,6 +2,8 @@ package com.simibubi.create;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
|
@ -152,6 +154,9 @@ public class Create {
|
||||||
|
|
||||||
// FIXME: this is not thread-safe
|
// FIXME: this is not thread-safe
|
||||||
Mods.CURIOS.executeIfInstalled(() -> () -> Curios.init(modEventBus, forgeEventBus));
|
Mods.CURIOS.executeIfInstalled(() -> () -> Curios.init(modEventBus, forgeEventBus));
|
||||||
|
|
||||||
|
if (FMLLoader.getDist().isDedicatedServer())
|
||||||
|
SCHEMATIC_RECEIVER.computeHashes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(final FMLCommonSetupEvent event) {
|
public static void init(final FMLCommonSetupEvent event) {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package com.simibubi.create.content.schematics;
|
||||||
|
|
||||||
|
public record SchematicFile(String playerName, String schematicName) {}
|
|
@ -57,6 +57,10 @@ public class SchematicItem extends Item {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ItemStack create(HolderGetter<Block> lookup, SchematicFile schematicFile) {
|
||||||
|
return create(lookup, schematicFile.schematicName(), schematicFile.playerName());
|
||||||
|
}
|
||||||
|
|
||||||
public static ItemStack create(HolderGetter<Block> lookup, String schematic, String owner) {
|
public static ItemStack create(HolderGetter<Block> lookup, String schematic, String owner) {
|
||||||
ItemStack blueprint = AllItems.SCHEMATIC.asStack();
|
ItemStack blueprint = AllItems.SCHEMATIC.asStack();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
package com.simibubi.create.content.schematics;
|
package com.simibubi.create.content.schematics;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -24,21 +27,27 @@ import com.simibubi.create.foundation.utility.Lang;
|
||||||
import com.simibubi.create.infrastructure.config.AllConfigs;
|
import com.simibubi.create.infrastructure.config.AllConfigs;
|
||||||
import com.simibubi.create.infrastructure.config.CSchematics;
|
import com.simibubi.create.infrastructure.config.CSchematics;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ReferenceOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.Util;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public class ServerSchematicLoader {
|
public class ServerSchematicLoader {
|
||||||
|
|
||||||
private Map<String, SchematicUploadEntry> activeUploads;
|
private Map<String, SchematicUploadEntry> activeUploads = new HashMap<>();
|
||||||
|
|
||||||
public class SchematicUploadEntry {
|
private Map<String, SchematicFile> sumToSchematic = new Object2ReferenceOpenHashMap<>();
|
||||||
|
|
||||||
|
public static class SchematicUploadEntry {
|
||||||
public Level world;
|
public Level world;
|
||||||
public BlockPos tablePos;
|
public BlockPos tablePos;
|
||||||
public OutputStream stream;
|
public OutputStream stream;
|
||||||
|
@ -56,19 +65,42 @@ public class ServerSchematicLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServerSchematicLoader() {
|
|
||||||
activeUploads = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSchematicPath() {
|
public String getSchematicPath() {
|
||||||
return "schematics/uploaded";
|
return "schematics/uploaded";
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ObjectArrayList<String> deadEntries = ObjectArrayList.of();
|
private final ObjectArrayList<String> deadEntries = ObjectArrayList.of();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public SchematicFile getSchematicFileFromSum(String sum) {
|
||||||
|
return sumToSchematic.get(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void computeHashes() {
|
||||||
|
Util.ioPool().submit(() -> {
|
||||||
|
try (Stream<Path> filePaths = Files.find(Path.of(getSchematicPath()), 2,
|
||||||
|
(filePath, attributes) -> filePath.toString().endsWith(".nbt"))) {
|
||||||
|
for (Path path : filePaths.toList()) {
|
||||||
|
try (InputStream stream = new FileInputStream(path.toFile())) {
|
||||||
|
String[] pathSplit = path.toString()
|
||||||
|
.replace("schematics/uploaded/", "")
|
||||||
|
.replace(".nbt", "")
|
||||||
|
.split("/");
|
||||||
|
String playerName = pathSplit[0];
|
||||||
|
String schematicName = pathSplit[1];
|
||||||
|
String schematicMd5Hex = DigestUtils.md5Hex(stream);
|
||||||
|
|
||||||
|
sumToSchematic.computeIfAbsent(schematicMd5Hex, k -> new SchematicFile(playerName, schematicName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
// Detect Timed out Uploads
|
// Detect Timed out Uploads
|
||||||
int timeout = getConfig().schematicIdleTimeout.get();
|
int timeout = getConfig().schematicIdleTimeout.get();
|
||||||
|
|
||||||
for (String upload : activeUploads.keySet()) {
|
for (String upload : activeUploads.keySet()) {
|
||||||
SchematicUploadEntry entry = activeUploads.get(upload);
|
SchematicUploadEntry entry = activeUploads.get(upload);
|
||||||
|
|
||||||
|
@ -82,6 +114,7 @@ public class ServerSchematicLoader {
|
||||||
for (String toRemove : deadEntries) {
|
for (String toRemove : deadEntries) {
|
||||||
this.cancelUpload(toRemove);
|
this.cancelUpload(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
deadEntries.clear();
|
deadEntries.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,17 +273,22 @@ public class ServerSchematicLoader {
|
||||||
table.finishUpload();
|
table.finishUpload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use when the schematic already exists on the server
|
||||||
|
public void useLocalFile(Level level, BlockPos pos, SchematicFile schematicFile) {
|
||||||
|
SchematicTableBlockEntity table = getTable(level, pos);
|
||||||
|
if (table != null) {
|
||||||
|
table.finishUpload();
|
||||||
|
table.inventory.setStackInSlot(1, SchematicItem.create(level.holderLookup(Registries.BLOCK), schematicFile));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SchematicTableBlockEntity getTable(Level world, BlockPos pos) {
|
public SchematicTableBlockEntity getTable(Level world, BlockPos pos) {
|
||||||
BlockEntity be = world.getBlockEntity(pos);
|
return world.getBlockEntity(pos) instanceof SchematicTableBlockEntity table ? table : null;
|
||||||
if (!(be instanceof SchematicTableBlockEntity))
|
|
||||||
return null;
|
|
||||||
SchematicTableBlockEntity table = (SchematicTableBlockEntity) be;
|
|
||||||
return table;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleFinishedUpload(ServerPlayer player, String schematic) {
|
public void handleFinishedUpload(ServerPlayer player, String schematic) {
|
||||||
String playerSchematicId = player.getGameProfile()
|
String playerName = player.getGameProfile().getName();
|
||||||
.getName() + "/" + schematic;
|
String playerSchematicId = playerName + "/" + schematic;
|
||||||
|
|
||||||
if (activeUploads.containsKey(playerSchematicId)) {
|
if (activeUploads.containsKey(playerSchematicId)) {
|
||||||
try {
|
try {
|
||||||
|
@ -259,6 +297,11 @@ public class ServerSchematicLoader {
|
||||||
Level world = removed.world;
|
Level world = removed.world;
|
||||||
BlockPos pos = removed.tablePos;
|
BlockPos pos = removed.tablePos;
|
||||||
|
|
||||||
|
// It'll be fine:tm:
|
||||||
|
try (InputStream stream = Files.newInputStream(Path.of(playerSchematicId), StandardOpenOption.READ)) {
|
||||||
|
sumToSchematic.computeIfAbsent(DigestUtils.md5Hex(stream), k -> new SchematicFile(playerName, schematic));
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
|
||||||
Create.LOGGER.info("New Schematic Uploaded: " + playerSchematicId);
|
Create.LOGGER.info("New Schematic Uploaded: " + playerSchematicId);
|
||||||
if (pos == null)
|
if (pos == null)
|
||||||
return;
|
return;
|
||||||
|
@ -271,9 +314,7 @@ public class ServerSchematicLoader {
|
||||||
if (table == null)
|
if (table == null)
|
||||||
return;
|
return;
|
||||||
table.finishUpload();
|
table.finishUpload();
|
||||||
table.inventory.setStackInSlot(1, SchematicItem.create(world.holderLookup(Registries.BLOCK), schematic, player.getGameProfile()
|
table.inventory.setStackInSlot(1, SchematicItem.create(world.holderLookup(Registries.BLOCK), schematic, playerName));
|
||||||
.getName()));
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Create.LOGGER.error("Exception Thrown when finishing Upload: " + playerSchematicId);
|
Create.LOGGER.error("Exception Thrown when finishing Upload: " + playerSchematicId);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
@ -28,6 +28,8 @@ import net.minecraft.network.chat.Component;
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
import org.apache.commons.codec.digest.DigestUtils;
|
||||||
|
|
||||||
@OnlyIn(Dist.CLIENT)
|
@OnlyIn(Dist.CLIENT)
|
||||||
public class ClientSchematicLoader {
|
public class ClientSchematicLoader {
|
||||||
|
|
||||||
|
@ -73,7 +75,11 @@ public class ClientSchematicLoader {
|
||||||
|
|
||||||
in = Files.newInputStream(path, StandardOpenOption.READ);
|
in = Files.newInputStream(path, StandardOpenOption.READ);
|
||||||
activeUploads.put(schematic, in);
|
activeUploads.put(schematic, in);
|
||||||
AllPackets.getChannel().sendToServer(SchematicUploadPacket.begin(schematic, size));
|
|
||||||
|
try (InputStream stream = Files.newInputStream(path, StandardOpenOption.READ)) {
|
||||||
|
String md5 = DigestUtils.md5Hex(stream);
|
||||||
|
AllPackets.getChannel().sendToServer(SchematicUploadPacket.begin(schematic, size, md5));
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.simibubi.create.content.schematics.packet;
|
package com.simibubi.create.content.schematics.packet;
|
||||||
|
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.content.schematics.SchematicFile;
|
||||||
import com.simibubi.create.content.schematics.table.SchematicTableMenu;
|
import com.simibubi.create.content.schematics.table.SchematicTableMenu;
|
||||||
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
import com.simibubi.create.foundation.networking.SimplePacketBase;
|
||||||
|
|
||||||
|
@ -9,6 +10,8 @@ import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraftforge.network.NetworkEvent.Context;
|
import net.minecraftforge.network.NetworkEvent.Context;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class SchematicUploadPacket extends SimplePacketBase {
|
public class SchematicUploadPacket extends SimplePacketBase {
|
||||||
|
|
||||||
public static final int BEGIN = 0;
|
public static final int BEGIN = 0;
|
||||||
|
@ -20,14 +23,17 @@ public class SchematicUploadPacket extends SimplePacketBase {
|
||||||
private String schematic;
|
private String schematic;
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
|
private String md5Hex;
|
||||||
|
|
||||||
public SchematicUploadPacket(int code, String schematic) {
|
public SchematicUploadPacket(int code, String schematic) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
this.schematic = schematic;
|
this.schematic = schematic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchematicUploadPacket begin(String schematic, long size) {
|
public static SchematicUploadPacket begin(String schematic, long size, String md5Hex) {
|
||||||
SchematicUploadPacket pkt = new SchematicUploadPacket(BEGIN, schematic);
|
SchematicUploadPacket pkt = new SchematicUploadPacket(BEGIN, schematic);
|
||||||
pkt.size = size;
|
pkt.size = size;
|
||||||
|
pkt.md5Hex = md5Hex;
|
||||||
return pkt;
|
return pkt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,8 +51,10 @@ public class SchematicUploadPacket extends SimplePacketBase {
|
||||||
code = buffer.readInt();
|
code = buffer.readInt();
|
||||||
schematic = buffer.readUtf(256);
|
schematic = buffer.readUtf(256);
|
||||||
|
|
||||||
if (code == BEGIN)
|
if (code == BEGIN) {
|
||||||
size = buffer.readLong();
|
size = buffer.readLong();
|
||||||
|
md5Hex = buffer.readUtf(32);
|
||||||
|
}
|
||||||
if (code == WRITE)
|
if (code == WRITE)
|
||||||
data = buffer.readByteArray();
|
data = buffer.readByteArray();
|
||||||
}
|
}
|
||||||
|
@ -56,8 +64,10 @@ public class SchematicUploadPacket extends SimplePacketBase {
|
||||||
buffer.writeInt(code);
|
buffer.writeInt(code);
|
||||||
buffer.writeUtf(schematic);
|
buffer.writeUtf(schematic);
|
||||||
|
|
||||||
if (code == BEGIN)
|
if (code == BEGIN) {
|
||||||
buffer.writeLong(size);
|
buffer.writeLong(size);
|
||||||
|
buffer.writeUtf(md5Hex);
|
||||||
|
}
|
||||||
if (code == WRITE)
|
if (code == WRITE)
|
||||||
buffer.writeByteArray(data);
|
buffer.writeByteArray(data);
|
||||||
}
|
}
|
||||||
|
@ -69,8 +79,28 @@ public class SchematicUploadPacket extends SimplePacketBase {
|
||||||
if (player == null)
|
if (player == null)
|
||||||
return;
|
return;
|
||||||
if (code == BEGIN) {
|
if (code == BEGIN) {
|
||||||
BlockPos pos = ((SchematicTableMenu) player.containerMenu).contentHolder
|
boolean usedLocalFile = false;
|
||||||
.getBlockPos();
|
|
||||||
|
BlockPos pos = ((SchematicTableMenu) player.containerMenu).contentHolder.getBlockPos();
|
||||||
|
|
||||||
|
SchematicFile schematicFile = Create.SCHEMATIC_RECEIVER.getSchematicFileFromSum(md5Hex);
|
||||||
|
|
||||||
|
if (schematicFile != null) {
|
||||||
|
String filePath = String.format(
|
||||||
|
"%s/%s/%s",
|
||||||
|
Create.SCHEMATIC_RECEIVER.getSchematicPath(),
|
||||||
|
schematicFile.playerName(),
|
||||||
|
schematicFile.schematicName()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Check if the file exists
|
||||||
|
if (new File(filePath).isFile()) {
|
||||||
|
Create.SCHEMATIC_RECEIVER.useLocalFile(player.level(), pos, schematicFile);
|
||||||
|
usedLocalFile = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usedLocalFile)
|
||||||
Create.SCHEMATIC_RECEIVER.handleNewUpload(player, schematic, size, pos);
|
Create.SCHEMATIC_RECEIVER.handleNewUpload(player, schematic, size, pos);
|
||||||
}
|
}
|
||||||
if (code == WRITE)
|
if (code == WRITE)
|
||||||
|
|
Loading…
Reference in a new issue